Compare commits

...
133 Commits
Author SHA1 Message Date
hajdam ec6506dae0 Release 0.2.10 2026-06-01 21:25:23 +02:00
hajdam b7361cb475 Minor fixes 2026-05-31 23:15:24 +02:00
hajdam f340758d89 Library update 2026-04-20 15:32:58 +02:00
Miroslav HajdaandGitHub 099ca8bb97 Merge pull request #30 from Genouka/master
Update Chinese strings for selection and search actions
2026-04-20 13:04:59 +02:00
GenoukaandGitHub f88dc25d1a Update Chinese translation credit in about_view.xml 2026-04-20 11:40:14 +08:00
GenoukaandGitHub 11691fa018 Update Chinese strings for selection and search actions 2026-04-20 11:34:47 +08:00
hajdam b4da69103e Dropped @author comment 2026-04-17 23:26:59 +02:00
hajdam 6d72439819 Greek translation cleanup 2025-11-01 17:24:19 +01:00
Miroslav HajdaandGitHub e02470e762 Merge pull request #28 from VisionR1/master
Add Greek Translation
2025-11-01 16:46:08 +01:00
VisionR1 651327f439 Add Greek Translation 2025-11-01 17:05:34 +02:00
hajdam a309ff8645 Version raised 2025-10-29 23:00:09 +01:00
hajdam 4068c8b735 Release 0.2.9 2025-10-29 21:17:16 +01:00
hajdam 74a1a7778e Minor fixes 2025-10-29 01:06:58 +01:00
hajdam 1488bc7276 Fix for requesting edgetoedge mode - 3 2025-10-28 21:03:25 +01:00
hajdam 16e976bfff Fix for requesting edgetoedge mode - 2 2025-10-28 20:17:18 +01:00
hajdam 4c3483f207 Fix for requesting edgetoedge mode 2025-10-28 19:51:07 +01:00
hajdam 2b9816ce80 Limit android library for F-droid build issue 2025-10-21 17:24:48 +02:00
hajdam a8f3cc126d Minor edit selection fix 2025-10-20 12:52:36 +02:00
Miroslav HajdaandGitHub 21d9c21a8c Merge pull request #25 from exbin/edit-selection-dialog
Added edit selection dialog
2025-10-19 23:38:16 +02:00
hajdam 1892bec80f Added edit selection dialog 2025-10-19 23:35:27 +02:00
hajdam 39496fd63f Version raised 2025-10-19 13:40:41 +02:00
hajdam 7f904c6e7f Release 0.2.8 2025-10-18 20:28:26 +02:00
Miroslav HajdaandGitHub 6a3ac364ab Merge pull request #24 from exbin/relative-go-to
Added go to position dialog
2025-10-17 23:21:08 +02:00
hajdam 5d84b5c254 Added go to position dialog 2025-10-17 23:16:04 +02:00
hajdam debc0a0410 Fixes for android TV keyboard handling 2025-10-15 23:19:28 +02:00
hajdam 4722f50a7c Library update 2025-10-04 00:34:20 +02:00
hajdam d671bf7446 Minor fixes 2025-08-30 20:25:55 +02:00
hajdam bec6f94b5f Minor changes 2025-08-16 23:24:07 +02:00
hajdam 4f35b77548 Minor changes 2025-08-11 23:11:10 +02:00
hajdam cbb63ff15f Target update 2025-07-04 23:08:16 +02:00
hajdam 772546291b Minor fix 2025-06-25 21:55:56 +02:00
hajdam 032aa9e5e0 Renamed to Portuguese (Brasil) 2025-06-20 00:12:05 +02:00
hajdam 6f5d152cbd Release 0.2.7 2025-06-19 17:58:33 +02:00
hajdam da66be3d57 Binary search - 3 2025-06-18 23:15:14 +02:00
hajdam 15940b4357 Library update 2025-06-18 00:09:19 +02:00
hajdam 1e9fdcae07 Added ru and es translations 2025-06-17 19:42:07 +02:00
hajdam 0a34b72a26 Binary search - 2 2025-06-16 23:44:07 +02:00
hajdam 24a19e3e2f Lazy day 2025-06-14 23:13:25 +02:00
hajdam 9362ce762c Binary search - 1 2025-06-11 23:44:06 +02:00
hajdam bfd2b19190 Added more download buttons - 5 2025-06-10 23:28:42 +02:00
hajdam c78381a20a Added more download buttons - 4 2025-06-10 23:21:31 +02:00
hajdam 06e16367f1 Added more download buttons - 3 2025-06-10 23:10:50 +02:00
hajdam 72565964f2 Added more download buttons - 2 2025-06-10 22:59:25 +02:00
hajdam 03edbba8c4 Added more download buttons 2025-06-10 22:57:09 +02:00
hajdam 908372c235 Minor fix 2025-06-10 21:28:11 +02:00
hajdam 4e3872519a Added contributor 2025-06-08 13:09:58 +02:00
hajdam 3f68527616 Added turkish language entry 2025-06-08 12:00:39 +02:00
Miroslav HajdaandGitHub c0487ce0c3 Merge pull request #23 from turkce-ceviriler/master
add turkish translation
2025-06-08 11:40:22 +02:00
Baturax 4dc8540461 add turkish 2025-06-08 11:57:53 +03:00
hajdam 9d40b98a4c Minor fix 2025-06-04 13:57:30 +02:00
hajdam 5c01dad173 Added Portuguese translation - 2 2025-05-30 19:20:46 +02:00
hajdam a5328702af Added Portuguese translation 2025-05-30 00:34:13 +02:00
hajdam fc17fe853e Fastlane update 2025-05-29 14:43:47 +02:00
hajdam 80fbed262d Release 0.2.6 2025-05-29 13:36:11 +02:00
hajdam a43e45f223 Added experimental support for tbl files - 3 2025-05-27 21:54:34 +02:00
hajdam e21069fd11 Added experimental support for tbl files - 2 2025-05-26 22:17:25 +02:00
hajdam 4d8552ac66 Added experimental support for tbl files 2025-05-25 20:13:33 +02:00
hajdam 2bcf3c6421 Library update 2025-05-25 14:42:35 +02:00
hajdam 9331dccb8b Library update 2025-05-22 18:16:21 +02:00
hajdam 424e9ce897 Libraries updated - 2 2025-05-13 00:22:54 +02:00
hajdam 5679c26e99 Libraries updated 2025-05-13 00:00:02 +02:00
hajdam d732892673 Attempt to mitigate PlayStore rejection - DPad issue 2025-05-09 19:50:52 +02:00
hajdam df32e610b1 Minor fix 2025-05-09 16:10:21 +02:00
hajdam 15a6e5b222 Added Chinese translation contributor 2025-05-09 14:15:37 +02:00
hajdam 89e0bb7cdc Language switching compatiblity fix 2025-05-08 18:32:48 +02:00
Miroslav HajdaandGitHub 69ab704151 Merge pull request #17 from WeiguangTWK/master
Correction in Language options
2025-05-08 17:54:41 +02:00
WeiguangTWK e11ce37438 Correct translation 2025-05-08 21:46:20 +08:00
hajdam db9c93bdf5 Version raised 2025-05-08 12:49:02 +02:00
Miroslav HajdaandGitHub ecb061f65c Merge pull request #16 from WeiguangTWK/master
Add Chinese-Simplified Translation
2025-05-08 11:59:20 +02:00
WeiguangTWK 21fe741eb8 Add Chinese-Simplified Translation in arrays.xml 2025-05-08 13:56:54 +08:00
WeiguangTWK 5167e4c9b5 Add Chinese-Simplified Translation in strings.xml 2025-05-08 13:56:37 +08:00
hajdam 44d6ee0df6 Release 0.2.5 2025-05-07 16:59:06 +02:00
hajdam ae3d07ebdd Double backpress exists app 2025-05-07 14:05:23 +02:00
hajdam a90fedbc4c Search threading 2025-05-07 03:13:10 +02:00
hajdam 97c6694fd2 Minor fixes / updated dependencies 2025-05-06 23:54:34 +02:00
hajdam b863035f09 Replaced app icon for playstore issue 2025-04-07 23:10:00 +02:00
hajdam 4e7279c766 Release 0.2.4 - target sdk fix 2025-04-06 16:25:07 +02:00
hajdam 16f8793308 Release 0.2.4 - fastlane update 2025-04-06 14:36:04 +02:00
hajdam 6f8388a0b5 Release 0.2.4 2025-04-06 14:30:43 +02:00
hajdam c02666d357 Extended compatibility to Android 4.0 / sdk14 - 3 2025-04-05 23:28:07 +02:00
hajdam 1df4d47ca6 Extended compatibility to Android 4.0 / sdk14 - 2 2025-04-05 00:25:12 +02:00
hajdam 0f9b9380cf Extended compatibility to Android 4.0 / sdk14 2025-04-04 23:56:39 +02:00
hajdam a6b38be5a8 Android gradle plugin update 2025-04-04 00:48:20 +02:00
hajdam 4775da61fc Gradle version raised 2025-02-22 11:08:04 +01:00
hajdam 2e306e87ff Minor refactoring 2024-12-13 23:44:17 +01:00
hajdam 8a71c42398 Binary data byte buffer - 6 2024-12-12 23:46:11 +01:00
hajdam 0c5e8ec482 DefaultCodeAreaCharAssessor update - 3 2024-12-06 19:36:52 +01:00
hajdam f082e7209c DefaultCodeAreaCharAssessor update - 2 2024-12-03 14:23:33 +01:00
hajdam 0e9412af7f DefaultCodeAreaCharAssessor update 2024-12-01 22:57:53 +01:00
hajdam 38e92e0fc3 Enabled some tests 2024-11-29 23:53:52 +01:00
hajdam 3e146185df Icon tint selector fix 2024-11-29 21:26:40 +01:00
hajdam 16c44bc7cd Rounded icon with less padding 2024-11-21 20:19:36 +01:00
hajdam 5ac5351dbe Version raised 2024-11-14 13:34:48 +01:00
hajdam dbcfa995fb Release 0.2.3 2024-11-13 23:14:27 +01:00
hajdam 16bc17921c Basic data inspector - 6 2024-11-13 18:05:27 +01:00
hajdam eb3b9f1e4a Basic data inspector - 5 2024-11-12 23:55:50 +01:00
hajdam f1f274f5af Basic data inspector - 4 2024-11-11 22:33:24 +01:00
hajdam 1b0d715004 Basic data inspector - 3 2024-11-10 21:54:30 +01:00
hajdam 98cceb8184 Reenabling minify 2024-11-07 14:38:37 +01:00
hajdam 7012192995 Basic data inspector - 2 2024-10-30 22:54:48 +01:00
hajdam 8d9e91693a Basic data inspector - 1 2024-10-29 22:50:15 +01:00
hajdam 604f967634 Lazy day 2024-10-28 23:43:31 +01:00
hajdam b7f5bf70bf Reworking action update - 26 2024-10-26 20:11:08 +02:00
hajdam dda92b2656 Options for button size in keys panel 2024-10-25 23:19:42 +02:00
Miroslav HajdaandGitHub 98f55f3554 Merge pull request #13 from DarioTD/patch-1
Fixed buttonAction9
2024-10-24 08:41:25 +02:00
Dario TabaresandGitHub 29f2ec28d6 Fixed buttonAction9
Now maps to 9 instead of 1
2024-10-23 23:59:27 -05:00
hajdam cf6ddbfebb Rework file picker - 2 2024-10-21 22:03:06 +02:00
hajdam 608e788673 Translation fix 2024-10-20 23:10:12 +02:00
hajdam 1f85b5941a Support for partial file opening - 3 2024-10-20 22:47:10 +02:00
hajdam beff277f6c Support for partial file opening - 2 2024-10-20 21:10:32 +02:00
hajdam 4d0e36932c Support for partial file opening 2024-10-18 22:31:52 +02:00
hajdam 75ee4dc363 Play store has issues with slash in app name 2024-10-17 23:34:17 +02:00
hajdam 490eee7146 Rework file picker - 1 2024-10-16 23:54:46 +02:00
hajdam 954f91410b Readme updated with play store download button 2024-10-16 14:03:53 +02:00
hajdam 7051207876 Version raised 2024-10-15 15:18:30 +02:00
Miroslav HajdaandGitHub 39475b6343 Merge pull request #12 from Poussinou/patch-1
Update README.md
2024-10-14 21:02:47 +02:00
PoussinouandGitHub 57044959b1 Update README.md 2024-10-14 14:36:58 -04:00
hajdam 721c5f0cdc Release 0.2.2 2024-10-13 15:57:31 +02:00
hajdam 3722d5d28a Use of application context - 2 2024-10-13 14:06:34 +02:00
hajdam 39eca4ceea Use of application context 2024-10-13 13:59:00 +02:00
hajdam 6691b83558 Rearranging operations - 6 2024-10-12 23:29:11 +02:00
hajdam 341fb4ddad Minor color tweak 2024-10-12 18:37:34 +02:00
hajdam eec740a735 Updated about dialog 2024-10-12 14:23:09 +02:00
hajdam eb14750c3f Basic D-pad support 2024-10-11 23:32:00 +02:00
hajdam 924d69bdc9 Fixed mirror cursor 2024-10-11 20:34:04 +02:00
hajdam 4340d3d3d3 Update from library + support for nonprintable characters 2024-10-10 19:33:40 +02:00
hajdam ee07614fe7 Gradle version update 2024-10-04 21:14:51 +02:00
hajdam 713b2d37d6 Fix for Hindi translation 2024-10-01 20:45:39 +02:00
Miroslav HajdaandGitHub 1c1d890f65 Merge pull request #10 from G0v1ndD3v/master
Completed Hindi translation.
2024-10-01 20:33:38 +02:00
govindscode 950699102d Completed Hindi translation. 2024-10-01 23:41:26 +05:30
hajdam 6b79cdc3d0 Legacy permission request 2024-09-29 20:14:32 +02:00
hajdam 2569575340 Handle open with 2024-09-28 23:10:36 +02:00
hajdam a542cb3cb4 Version raised 2024-09-28 20:14:48 +02:00
699 changed files with 21159 additions and 3591 deletions
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
<bytecodeTargetLevel target="21" />
</component>
</project>
+2 -1
View File
@@ -4,13 +4,14 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/file_picker" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
+8 -3
View File
@@ -10,12 +10,13 @@
</map>
</option>
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="15">
<list size="17">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
@@ -31,12 +32,14 @@
<item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" />
<item index="14" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" />
<item index="15" class="java.lang.String" itemvalue="org.jspecify.annotations.Nullable" />
<item index="16" class="java.lang.String" itemvalue="jakarta.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="15">
<list size="17">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
@@ -52,11 +55,13 @@
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
<item index="14" class="java.lang.String" itemvalue="lombok.NonNull" />
<item index="15" class="java.lang.String" itemvalue="org.jspecify.annotations.NonNull" />
<item index="16" class="java.lang.String" itemvalue="jakarta.annotation.Nonnull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>
+1 -1
View File
@@ -6,7 +6,7 @@ When contributing to this repository, it's recommended to first discuss the chan
Please see project's webpage for some additional information:
https://bined.exbin.org/?participate
https://bined.exbin.org/?p=participate
## Development
+21 -7
View File
@@ -1,10 +1,14 @@
BinEd - Binary/Hex Editor
=========================
BinEd - Hex Editor
==================
Editor for binary data (hex viewer/editor) written in Java.
Homepage: https://bined.exbin.org/android
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/packages/org.exbin.bined.editor.android/)[<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="80">](https://play.google.com/store/apps/details?id=org.exbin.bined.editor.android)[<img src="https://github.com/user-attachments/assets/713d71c5-3dec-4ec4-a3f2-8d28d025a9c6" alt="Get it on Obtainium" height="80">](http://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22org.exbin.bined.editor.android%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fexbin%2Fbined-android%22%2C%22author%22%3A%22ExBin%20Project%22%2C%22name%22%3A%22BinEd%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Afalse%7D%22%7D)[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyDroid" height="80">](https://android.izzysoft.de/repo/apk/org.exbin.bined.editor.android)
Or download the latest APK from the [Releases Section](https://github.com/exbin/bined-android/releases/latest).
Screenshot
----------
@@ -13,22 +17,32 @@ Screenshot
Features
--------
UNDER DEVELOPMENT
* Visualize data as numerical (hexadecimal) codes and text representation
* Codes can be also binary, octal or decimal
* Support for Unicode, UTF-8 and other charsets
* Insert and overwrite edit modes
* TODO: Searching for text / hexadecimal code with matching highlighting
* Searching for text / hexadecimal code with matches highlighting
* Support for undo/redo
* TODO: Support for files with size up to exabytes
* Support for files with size up to exabytes (in partial file mode)
Compiling
---------
To build this project, Android Studio is recommended.
To compile project and build apk you need installed Java JDK 17 or later and run:
./gradlew assembleRelease
or on Windows:
gradlew.bat assembleRelease
To modify this project, Android Studio is recommended.
https://developer.android.com/studio/
When using Android Studio modify `playStore = false` line in build.gradle file to avoid legacy errors:
playStore = true
License
-------
+65 -17
View File
@@ -2,45 +2,93 @@ plugins {
id 'com.android.application'
}
def playStore = rootProject.ext.playstore
// For support of SDK 14, newer material versions requests fails to build
def materialVersion = '1.6.1'
android {
compileSdk 34
namespace 'org.exbin.bined.editor.android'
if (playstore) {
compileSdk {
version = release(35)
}
} else {
compileSdk 35
}
defaultConfig {
applicationId "org.exbin.bined.editor.android"
minSdk 27
targetSdk 34
versionCode 5
versionName "0.2.1"
if (playStore) {
minSdk 24
materialVersion = '1.13.0'
} else {
minSdk 14
multiDexEnabled true
// sourceSets {
// main.java.srcDirs += 'src/legacy'
// }
}
targetSdk 35
versionCode 19
versionName "0.2.10"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
vectorDrawables.generatedDensities = []
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
minifyEnabled true
// shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main.java.srcDirs += 'libsrc'
}
namespace 'org.exbin.bined.editor.android'
compileOptions {
// Flag to enable support for the new language APIs
// coreLibraryDesugaringEnabled true
// sourceCompatibility JavaVersion.VERSION_1_8
// targetCompatibility JavaVersion.VERSION_1_8
}
lint {
checkReleaseBuilds false
}
}
tasks.withType(JavaCompile) {
tasks.withType(JavaCompile).configureEach {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.7.0'
// implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.flexbox:flexbox:3.0.0'
compileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
// implementation 'com.github.rustamg:file-dialogs:1.0'
implementation 'com.google.android.material:material:1.12.0'
implementation "com.google.android.material:material:${materialVersion}"
// coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
implementation 'androidx.preference:preference:1.2.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
implementation 'net.java.dev.jna:jna:5.18.1@aar'
implementation project(':file_picker')
// implementation 'org.exbin.bined:bined-core:0.3.0-SNAPSHOT'
// implementation 'org.exbin.bined:bined-operation:0.3.0-SNAPSHOT'
implementation 'org.exbin.auxiliary:binary_data:0.2.1'
// implementation 'org.exbin.auxiliary:binary_data-paged:0.2.1'
// implementation 'org.exbin.auxiliary:binary_data:0.2.1'
// implementation 'org.exbin.auxiliary:binary_data-array:0.2.1'
// implementation 'org.exbin.auxiliary:binary_data-delta:0.2.1'
testCompileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
testImplementation 'junit:junit:4.13.2'
testImplementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation 'androidx.test:core:1.7.0'
testImplementation 'androidx.test.ext:junit:1.3.0'
testImplementation "androidx.test.espresso:espresso-core:3.7.0";
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation "androidx.test:runner:1.7.0";
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
}
@@ -0,0 +1,102 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface for binary data - readable sequence of bytes.
* <p>
* Provides methods to read whole or part of the data to array or stream.
*/
@ParametersAreNonnullByDefault
public interface BinaryData {
/**
* Returns true if data are empty.
*
* @return true if data empty
*/
boolean isEmpty();
/**
* Returns size of data or -1 if size is not available.
*
* @return size of data in bytes
*/
long getDataSize();
/**
* Returns byte data on the given position.
*
* @param position position
* @return byte on requested position
*/
byte getByte(long position);
/**
* Creates copy of all data.
*
* @return copy of data
*/
@Nonnull
BinaryData copy();
/**
* Creates copy of given area.
*
* @param startFrom position to start copy from
* @param length length of area
* @return copy of data
*/
@Nonnull
BinaryData copy(long startFrom, long length);
/**
* Creates copy of given area into array of bytes.
*
* @param startFrom position to start copy from
* @param target target byte array
* @param offset offset position in target
* @param length length of area to copy
*/
void copyToArray(long startFrom, byte[] target, int offset, int length);
/**
* Saves/copies all data to given stream.
*
* @param outputStream output stream
* @throws java.io.IOException if input/output error
*/
void saveToStream(OutputStream outputStream) throws IOException;
/**
* Provides handler for input stream generation.
*
* @return new instance of input stream
*/
@Nonnull
InputStream getDataInputStream();
/**
* Disposes all allocated data if possible.
*/
void dispose();
}
@@ -0,0 +1,123 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Input stream for binary data.
* <p>
* Can process expanding data source.
*/
@ParametersAreNonnullByDefault
public class BinaryDataInputStream extends InputStream implements SeekableStream {
@Nonnull
protected final BinaryData data;
protected long position = 0;
protected long mark = 0;
public BinaryDataInputStream(BinaryData data) {
this.data = data;
}
@Override
public int read() throws IOException {
if (position > data.getDataSize()) {
return -1;
}
try {
return data.getByte(position++) & 0xFF;
} catch (ArrayIndexOutOfBoundsException ex) {
return -1;
}
}
@Override
public int read(byte[] output, int off, int len) throws IOException {
if (output.length == 0 || len == 0) {
return 0;
}
long dataSize = data.getDataSize();
if (position > dataSize - len) {
if (position >= dataSize) {
return -1;
}
len = (int) (dataSize - position);
}
data.copyToArray(position, output, off, len);
position += len;
return len;
}
@Override
public void close() throws IOException {
position = data.getDataSize();
}
@Override
public int available() throws IOException {
return (int) (data.getDataSize() - position);
}
@Override
public void seek(long position) throws IOException {
this.position = position;
}
public long getProcessedSize() {
return position;
}
@Override
public long getStreamSize() {
return -1;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void reset() throws IOException {
position = mark;
}
@Override
public synchronized void mark(int readlimit) {
mark = position;
}
@Override
public long skip(long n) throws IOException {
long dataSize = data.getDataSize();
if (position + n < dataSize) {
position += n;
return n;
}
long skipped = dataSize - position;
position = dataSize;
return skipped;
}
}
@@ -0,0 +1,92 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Output stream for binary data.
* <p>
* Data are expanded as needed.
*/
@ParametersAreNonnullByDefault
public class BinaryDataOutputStream extends OutputStream implements SeekableStream {
@Nonnull
protected final EditableBinaryData data;
protected long position = 0;
public BinaryDataOutputStream(EditableBinaryData data) {
this.data = data;
}
@Override
public void write(int value) throws IOException {
long dataSize = data.getDataSize();
if (position == dataSize) {
dataSize++;
data.insertUninitialized(position, 1);
}
data.setByte(position++, (byte) value);
}
@Override
public void write(byte[] input, int off, int len) throws IOException {
if (len == 0) {
return;
}
long dataSize = data.getDataSize();
if (position + len > dataSize) {
long expand = position + len - dataSize;
data.insertUninitialized(position, expand);
}
data.replace(position, input, off, len);
position += len;
}
@Override
public void seek(long position) throws IOException {
if (position < 0) {
throw new OutOfBoundsException("Position is outside of available range");
}
if (position > data.getDataSize()) {
data.setDataSize(position);
}
this.position = position;
}
@Override
public long getStreamSize() {
return -1;
}
public long getProcessedSize() {
return position;
}
@Override
public void close() throws IOException {
position = data.getDataSize();
}
}
@@ -0,0 +1,157 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Input stream for binary data range.
*/
@ParametersAreNonnullByDefault
public class BinaryDataRangeInputStream extends InputStream implements SeekableStream, FinishableStream {
@Nonnull
protected final BinaryData data;
protected final long startPosition;
protected final long length;
protected long position = 0;
protected long mark = 0;
public BinaryDataRangeInputStream(BinaryData data) {
this.data = data;
this.startPosition = 0;
this.position = 0;
this.length = data.getDataSize();
}
public BinaryDataRangeInputStream(BinaryData data, DataRange dataRange) {
this(data, dataRange.startPosition, dataRange.getLength());
}
public BinaryDataRangeInputStream(BinaryData data, long startPosition, long length) {
if (startPosition < 0) {
throw new IllegalArgumentException("Negative position not allowed");
}
if (length < 0) {
throw new IllegalArgumentException("Negative length not allowed");
}
long sourceDataSize = data.getDataSize();
if (startPosition + length > sourceDataSize) {
throw new OutOfBoundsException("Target area is outside of available data");
}
this.data = data;
this.startPosition = startPosition;
this.position = startPosition;
this.length = length;
}
@Override
public int read() throws IOException {
if (position > startPosition + length) {
return -1;
}
try {
return data.getByte(position++) & 0xFF;
} catch (ArrayIndexOutOfBoundsException ex) {
return -1;
}
}
@Override
public int read(byte[] output, int off, int len) throws IOException {
if (output.length == 0 || len == 0) {
return 0;
}
if (position > startPosition + length - len) {
if (position >= startPosition + length) {
return -1;
}
len = (int) (startPosition + length - position);
}
data.copyToArray(position, output, off, len);
position += len;
return len;
}
@Override
public void close() throws IOException {
finish();
}
@Override
public int available() throws IOException {
return (int) ((startPosition + length) - position);
}
@Override
public void seek(long position) throws IOException {
if (position < 0 || position > length) {
throw new OutOfBoundsException("Position is outside of available range");
}
this.position = startPosition + position;
}
@Override
public long finish() throws IOException {
position = startPosition + length;
return length;
}
@Override
public long getProcessedSize() {
return position - startPosition;
}
@Override
public long getStreamSize() {
return length;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void reset() throws IOException {
position = mark;
}
@Override
public synchronized void mark(int readlimit) {
mark = position;
}
@Override
public long skip(long n) throws IOException {
if (position - startPosition + n < length) {
position += n;
return n;
}
long skipped = startPosition + length - position;
position = startPosition + length;
return skipped;
}
}
@@ -0,0 +1,114 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Output stream for binary data.
*/
@ParametersAreNonnullByDefault
public class BinaryDataRengeOutputStream extends OutputStream implements SeekableStream, FinishableStream {
@Nonnull
protected final EditableBinaryData data;
protected final long startPosition;
protected final long length;
protected long position = 0;
public BinaryDataRengeOutputStream(EditableBinaryData data) {
this.data = data;
this.startPosition = 0;
this.length = data.getDataSize();
}
public BinaryDataRengeOutputStream(EditableBinaryData data, DataRange dataRange) {
this(data, dataRange.startPosition, dataRange.getLength());
}
public BinaryDataRengeOutputStream(EditableBinaryData data, long startPosition, long length) {
if (startPosition < 0) {
throw new IllegalArgumentException("Negative position not allowed");
}
if (length < 0) {
throw new IllegalArgumentException("Negative length not allowed");
}
long sourceDataSize = data.getDataSize();
if (startPosition + length > sourceDataSize) {
throw new OutOfBoundsException("Target area is outside of available data");
}
this.data = data;
this.startPosition = startPosition;
this.position = startPosition;
this.length = length;
}
@Override
public void write(int value) throws IOException {
if (position >= startPosition + length) {
throw new OutOfBoundsException("Position is outside of available range");
}
data.setByte(position++, (byte) value);
}
@Override
public void write(byte[] input, int off, int len) throws IOException {
if (len == 0) {
return;
}
if (position + len > startPosition + length) {
throw new OutOfBoundsException("Target area is outside of available data");
}
data.replace(position, input, off, len);
position += len;
}
@Override
public void seek(long position) throws IOException {
if (position < 0 || position > length) {
throw new OutOfBoundsException("Position is outside of available range");
}
this.position = startPosition + position;
}
@Override
public long getStreamSize() {
return length;
}
@Override
public long getProcessedSize() {
return position - startPosition;
}
@Override
public void close() throws IOException {
finish();
}
@Override
public long finish() throws IOException {
position = startPosition + length;
return length;
}
}
@@ -0,0 +1,70 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Exception for data reading or writing when range of data is not present.
* <p>
* Exception can optionally provide information about range of data which are
* not present, but it doesn't have to be full segment.
*/
public class DataNotPresentException extends RuntimeException {
@Nullable
protected final DataRange dataRange;
public DataNotPresentException() {
dataRange = null;
}
public DataNotPresentException(@Nullable DataRange dataRange) {
this.dataRange = dataRange;
}
public DataNotPresentException(@Nullable String message) {
super(message);
dataRange = null;
}
public DataNotPresentException(@Nullable String message, @Nullable DataRange dataRange) {
super(message);
this.dataRange = dataRange;
}
public DataNotPresentException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause);
dataRange = null;
}
public DataNotPresentException(@Nullable Throwable cause) {
super(cause);
dataRange = null;
}
public DataNotPresentException(@Nullable String message, @Nullable Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
dataRange = null;
}
@Nonnull
public Optional<DataRange> getDataRange() {
return Optional.ofNullable(dataRange);
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import javax.annotation.Nullable;
/**
* Exception for overflow situation where more data is inserted/added than it is
* allowed to handle.
*/
public class DataOverflowException extends RuntimeException {
public DataOverflowException() {
}
public DataOverflowException(@Nullable String message) {
super(message);
}
public DataOverflowException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause);
}
public DataOverflowException(@Nullable Throwable cause) {
super(cause);
}
public DataOverflowException(@Nullable String message, @Nullable Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import javax.annotation.concurrent.Immutable;
/**
* Simple representation of data range.
* <p>
* Range includes both start and end positions.
*/
@Immutable
public class DataRange {
protected final long startPosition;
protected final long endPosition;
public DataRange(long startPosition, long endPosition) {
if (endPosition > startPosition) {
throw new IllegalStateException("Invalid data range");
}
this.startPosition = startPosition;
this.endPosition = endPosition;
}
public long getStartPosition() {
return startPosition;
}
public long getEndPosition() {
return endPosition;
}
public long getLength() {
return endPosition - startPosition + 1;
}
}
@@ -0,0 +1,213 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface for editable binary data - sequence of bytes.
* <p>
* Provides methods to read/write whole or part of the data to array or stream.
*/
@ParametersAreNonnullByDefault
public interface EditableBinaryData extends BinaryData {
/**
* Sets data size.
* <p>
* If size is bigger than current size, it will fill it with zeros,
* otherwise it will shrink current data.
*
* @param size target size
*/
void setDataSize(long size);
/**
* Sets byte data on the given position.
*
* @param position position
* @param value byte value to be set
*/
void setByte(long position, byte value);
/**
* Inserts data space of given length to given position without setting any
* data to it.
*
* @param startFrom position to insert to
* @param length length of data
*/
void insertUninitialized(long startFrom, long length);
/**
* Inserts empty data of given length to given position.
*
* @param startFrom position to insert to
* @param length length of data
*/
void insert(long startFrom, long length);
/**
* Inserts given data to given position.
*
* @param startFrom position to insert to
* @param insertedData data to insert
*/
void insert(long startFrom, byte[] insertedData);
/**
* Inserts given data to given position.
*
* @param startFrom position to insert to
* @param insertedData data to insert
* @param insertedDataOffset inserted data offset
* @param insertedDataLength inserted data length
*/
void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength);
/**
* Inserts given data to given position.
*
* @param startFrom position to insert to
* @param insertedData data to insert
*/
void insert(long startFrom, BinaryData insertedData);
/**
* Inserts given data to given position.
*
* @param startFrom position to insert to
* @param insertedData data to insert
* @param insertedDataOffset inserted data offset
* @param insertedDataLength inserted data length
*/
void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength);
/**
* Loads data from given stream expecting provided size.
* <p>
* Preserves original data outside loaded range. Extends data if needed.
*
* @param startFrom start position to insert data
* @param inputStream input stream
* @param maximumDataSize size of data to load or -1 for all data
* @return length of loaded data
* @throws java.io.IOException if input/output error
*/
long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException;
/**
* Replaces data in given area with provided data.
* <p>
* If sourceData are the same instance, data are replaced as it would be
* copied to buffer first and replaced then.
*
* @param targetPosition target position to write to
* @param replacingData data to read from
*/
void replace(long targetPosition, BinaryData replacingData);
/**
* Replaces data in given area with provided data.
* <p>
* If sourceData are the same instance, data are replaced as it would be
* copied to buffer first and replaced then.
*
* @param targetPosition target position to write to
* @param replacingData data to read from
* @param startFrom position to start copy from
* @param length length of data to copy
*/
void replace(long targetPosition, BinaryData replacingData, long startFrom, long length);
/**
* Replaces data in given area with provided data.
* <p>
* If sourceData are the same instance, data are replaced as it would be
* copied to buffer first and replaced then.
*
* @param targetPosition target position to write to
* @param replacingData data to read from
*/
void replace(long targetPosition, byte[] replacingData);
/**
* Replaces data in given area with provided data.
* <p>
* If sourceData are the same instance, data are replaced as it would be
* copied to buffer first and replaced then.
*
* @param targetPosition target position to write to
* @param replacingData data to read from
* @param replacingDataOffset position to start copy from
* @param length length of data to copy
*/
void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length);
/**
* Fills given area with empty data.
*
* @param startFrom position to fill data to
* @param length length of area
*/
void fillData(long startFrom, long length);
/**
* Fills given area with bytes of given value.
*
* @param startFrom position to fill data to
* @param length length of area
* @param fill value to fill with
*/
void fillData(long startFrom, long length, byte fill);
/**
* Removes area of data.
*
* @param startFrom position to start removal from
* @param length length of area
*/
void remove(long startFrom, long length);
/**
* Removes all existing data.
* <p>
* Simply releases all references to data pages.
*/
void clear();
/**
* Replaces all data by data red from given stream.
*
* @param inputStream input stream
* @throws java.io.IOException if input/output error
*/
void loadFromStream(InputStream inputStream) throws IOException;
/**
* Provides handler for output stream generation.
* <p>
* Received data are appended to existing data.
*
* @return new instance of output stream
*/
@Nonnull
OutputStream getDataOutputStream();
}
@@ -0,0 +1,100 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Empty read-only binary data.
*/
@ParametersAreNonnullByDefault
@Immutable
public class EmptyBinaryData implements BinaryData {
private static final class Singleton {
private static final EmptyBinaryData INSTANCE = new EmptyBinaryData();
}
@Nonnull
public static EmptyBinaryData getInstance() {
return Singleton.INSTANCE;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public long getDataSize() {
return 0;
}
@Override
public byte getByte(long position) {
throw new OutOfBoundsException();
}
@Nonnull
@Override
public BinaryData copy() {
return this;
}
@Nonnull
@Override
public BinaryData copy(long startFrom, long length) {
if (startFrom == 0 && length == 0) {
return this;
}
throw new OutOfBoundsException();
}
@Override
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
if (startFrom == 0 && length == 0) {
return;
}
throw new OutOfBoundsException();
}
@Override
public void saveToStream(OutputStream outputStream) throws IOException {
}
@Nonnull
@Override
public InputStream getDataInputStream() {
return new InputStream() {
@Override
public int read() throws IOException {
return -1;
}
};
}
@Override
public void dispose() {
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
/**
* Interface for finish-able stream - allows to skip/process data to identify
* size of remaining data.
*/
public interface FinishableStream {
/**
* Reads or writes remaining data and returns size of all data processed by
* this stream.
*
* @return size of data in bytes
* @throws IOException if input/output error occurs
*/
long finish() throws IOException;
/**
* Returns size of data processed so far.
*
* @return size of data in bytes
*/
long getProcessedSize();
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import javax.annotation.Nullable;
/**
* Exception for out of bounds issues with binary data manipulation operations.
*/
public class OutOfBoundsException extends RuntimeException {
public OutOfBoundsException() {
}
public OutOfBoundsException(@Nullable String message) {
super(message);
}
public OutOfBoundsException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause);
}
public OutOfBoundsException(@Nullable Throwable cause) {
super(cause);
}
public OutOfBoundsException(@Nullable String message, @Nullable Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@@ -0,0 +1,40 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
import java.io.IOException;
/**
* Interface for seekable stream - allows to change current position.
*/
public interface SeekableStream {
/**
* Moves position in the stream to given position from the start of the
* stream.
*
* @param position target position
* @throws IOException if input/output error
*/
void seek(long position) throws IOException;
/**
* Returns length of the stream.
*
* @return length of the stream in bytes, -1 if unable to determine
*/
long getStreamSize();
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data;
/**
* Interface for binary data with missing segments / segments where no data are
* present.
* <p>
* Expected use is to either check presence per position or segments or to read
* data and handle presence only when {@link DataNotPresentException} is thrown.
*/
public interface SparseBinaryData {
/**
* Returns true if data are present on given position.
*
* @param position position
* @return true if present
*/
boolean isDataPresent(long position);
/**
* Returns closest next subsequent data position where data presence changes
* or any position closer, but greater than given position.
* <p>
* Basic implementation can return position + 1 which forces reading and
* handling of {@link DataNotPresentException}.
*
* @param position position to check from
* @return next position to check for presence
*/
long closestNextPossiblePresenceChange(long position);
/**
* Returns closest previous preceding data position where data presence
* changes or any position closer, but lesser than given position.
* <p>
* Basic implementation can return position - 1 which forces reading and
* handling of {@link DataNotPresentException}.
*
* @param position position to check from
* @return previous position to check for presence
*/
long closestPreviousPossiblePresenceChange(long position);
}
@@ -0,0 +1,239 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.buffer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.BinaryDataInputStream;
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
/**
* Implementation of binary data interface using byte buffer.
* <p>
* To allow parallel reading, read operations must be synchronized due to split
* nature of the ByteBuffer position and depending operation.
*/
@ParametersAreNonnullByDefault
public class BufferData implements BinaryData {
protected static final int BUFFER_SIZE = 4096;
@Nonnull
protected ByteBuffer data;
public BufferData() {
this((ByteBuffer) null);
}
/**
* Creates instance directly wrapping provided byte buffer.
*
* @param data byte buffer
*/
public BufferData(@Nullable ByteBuffer data) {
this.data = data != null ? data : BufferData.this.allocateBuffer(0);
}
/**
* Creates instance setting value to provided byte array.
*
* @param data byte array
*/
public BufferData(@Nullable byte[] data) {
if (data == null) {
this.data = BufferData.this.allocateBuffer(0);
} else {
this.data = BufferData.this.allocateBuffer(data.length);
this.data.put(data);
this.data.clear();
}
}
/**
* Creates instance with specified size.
*
* @param dataSize data size
*/
public BufferData(int dataSize) {
this.data = BufferData.this.allocateBuffer(dataSize);
}
/**
* Returns internal data.
*
* @return byte array
*/
@Nonnull
public ByteBuffer getData() {
return data;
}
@Override
public boolean isEmpty() {
return data.capacity() == 0;
}
@Override
public long getDataSize() {
return data.capacity();
}
@Override
public synchronized byte getByte(long position) {
try {
return data.get((int) position);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
}
@Nonnull
@Override
public BufferData copy() {
ByteBuffer copy = allocateBuffer(data.capacity());
synchronized (this) {
data.rewind();
copy.put(data);
}
return new BufferData(copy);
}
@Nonnull
@Override
public BufferData copy(long startFrom, long length) {
if (length > Integer.MAX_VALUE) {
throw new OutOfBoundsException("Buffer data is limited by integer length");
}
if (startFrom + length > data.capacity()) {
throw new OutOfBoundsException("Attempt to copy outside of data");
}
ByteBuffer copy = allocateBuffer((int) length);
synchronized (this) {
data.position((int) startFrom);
data.limit((int) (startFrom + length));
copy.put(data);
data.clear();
return new BufferData(copy);
}
}
@Override
public synchronized void copyToArray(long startFrom, byte[] target, int offset, int length) {
try {
data.position((int) startFrom);
data.get(target, offset, length);
} catch (IndexOutOfBoundsException | BufferUnderflowException | IllegalArgumentException ex) {
throw new OutOfBoundsException(ex);
}
}
@Override
public void saveToStream(OutputStream outputStream) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int remaining = data.capacity();
int position = 0;
while (remaining > 0) {
int length = remaining > BUFFER_SIZE ? BUFFER_SIZE : remaining;
synchronized (this) {
data.position(position);
data.get(buffer, 0, length);
}
outputStream.write(buffer, 0, length);
position += length;
remaining -= length;
}
}
@Nonnull
@Override
public InputStream getDataInputStream() {
return new BinaryDataInputStream(this);
}
@Override
public synchronized int hashCode() {
return data.hashCode();
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
if (obj instanceof BinaryData) {
BinaryData other = (BinaryData) obj;
return compareTo(other);
}
return false;
}
final BufferData other = (BufferData) obj;
synchronized (this) {
other.data.rewind();
data.rewind();
return other.data.compareTo(data) == 0;
}
}
public boolean compareTo(BinaryData other) {
long dataSize = getDataSize();
if (other.getDataSize() != dataSize) {
return false;
}
int bufferSize = dataSize > BUFFER_SIZE ? BUFFER_SIZE : (int) dataSize;
byte[] buffer = new byte[bufferSize];
int offset = 0;
int remain = (int) dataSize;
while (remain > 0) {
int length = remain > bufferSize ? bufferSize : remain;
other.copyToArray(offset, buffer, 0, length);
for (int i = 0; i < length; i++) {
if (data.get(offset + i) != buffer[i]) {
return false;
}
}
offset += length;
remain -= length;
}
return true;
}
@Override
public void dispose() {
}
@Nonnull
protected ByteBuffer allocateBuffer(int capacity) {
return ByteBuffer.allocate(capacity);
}
}
@@ -0,0 +1,393 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.buffer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.security.InvalidParameterException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.BinaryDataOutputStream;
import org.exbin.auxiliary.binary_data.DataOverflowException;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
/**
* Implementation of editable binary data interface using byte buffer.
*/
@ParametersAreNonnullByDefault
public class BufferEditableData extends BufferData implements EditableBinaryData {
public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 5;
private static final String WRONG_INSERTION_POSITION_ERROR = "Data can be inserted only inside or at the end";
private static final String WRONG_REPLACE_POSITION_ERROR = "Data can be replaced only inside or at the end";
private static final String ARRAY_OVERFLOW_ERROR = "Maximum array size overflow";
public BufferEditableData() {
this((ByteBuffer) null);
}
public BufferEditableData(int dataSize) {
super(dataSize);
}
public BufferEditableData(@Nullable ByteBuffer data) {
super(data);
}
public BufferEditableData(@Nullable byte[] data) {
super(data);
}
@Override
public void setDataSize(long size) {
if (size < 0) {
throw new InvalidParameterException("Size cannot be negative");
}
int oldSize = data.capacity();
if (oldSize != size) {
ByteBuffer newData = allocateBuffer((int) size);
if (size < oldSize) {
if (size > 0) {
data.rewind();
data.limit((int) size);
newData.put(data);
data.limit(oldSize);
}
data = newData;
} else {
if (oldSize > 0) {
data.rewind();
newData.put(data);
}
data = newData;
}
}
}
@Override
public void setByte(long position, byte value) {
try {
data.put((int) position, value);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
}
@Override
public void insertUninitialized(long startFrom, long length) {
if (startFrom > data.capacity()) {
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
}
if (length > MAX_ARRAY_LENGTH - data.capacity()) {
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
}
if (length > 0) {
ByteBuffer newData = allocateBuffer((int) (data.capacity() + length));
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
BufferEditableData.put(newData, (int) (startFrom + length), data, (int) startFrom, (int) (data.capacity() - startFrom));
data = newData;
}
}
@Override
public void insert(long startFrom, long length) {
insertUninitialized(startFrom, length);
fillData(startFrom, length, (byte) 0);
}
@Override
public void insert(long startFrom, byte[] insertedData) {
if (startFrom > data.capacity()) {
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
}
if (insertedData.length > MAX_ARRAY_LENGTH - data.capacity()) {
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
}
int length = insertedData.length;
if (length > 0) {
ByteBuffer newData = allocateBuffer((int) (data.capacity() + length));
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
try {
newData.position((int) startFrom);
newData.put(insertedData);
BufferEditableData.put(newData, (int) (startFrom + length), data, (int) startFrom, (int) (data.capacity() - startFrom));
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
data = newData;
}
}
@Override
public void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int length) {
if (startFrom > data.capacity()) {
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
}
if (length > MAX_ARRAY_LENGTH - data.capacity()) {
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
}
if (length > 0) {
ByteBuffer newData = allocateBuffer((int) (data.capacity() + length));
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
try {
newData.position((int) startFrom);
newData.put(insertedData, insertedDataOffset, length);
BufferEditableData.put(newData, (int) (startFrom + length), data, (int) startFrom, (int) (data.capacity() - startFrom));
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
data = newData;
}
}
@Override
public void insert(long startFrom, BinaryData insertedData) {
if (startFrom > data.capacity()) {
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
}
if (insertedData.getDataSize() > MAX_ARRAY_LENGTH - data.capacity()) {
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
}
insert(startFrom, insertedData, 0, insertedData.getDataSize());
}
@Override
public void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
if (startFrom > data.capacity()) {
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
}
if (insertedDataLength > MAX_ARRAY_LENGTH - data.capacity()) {
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
}
long length = insertedDataLength;
if (length > 0) {
ByteBuffer newData = allocateBuffer((int) (data.capacity() + length));
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
for (int i = 0; i < length; i++) {
newData.position((int) startFrom + i);
newData.put(insertedData.getByte(insertedDataOffset + i));
}
BufferEditableData.put(newData, (int) (startFrom + length), data, (int) startFrom, (int) (data.capacity() - startFrom));
data = newData;
}
}
@Override
public long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException {
if (maximumDataSize > MAX_ARRAY_LENGTH - data.capacity()) {
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
}
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[BUFFER_SIZE];
while (maximumDataSize == -1 || maximumDataSize > 0) {
int toRead = buffer.length;
if (maximumDataSize >= 0 && toRead > maximumDataSize) {
toRead = (int) maximumDataSize;
}
int read = inputStream.read(buffer, 0, toRead);
if (read == -1) {
break;
}
if (read > 0) {
output.write(buffer, 0, read);
if (maximumDataSize >= 0) {
maximumDataSize -= read;
}
}
}
byte[] newData = output.toByteArray();
if (startFrom + newData.length > getDataSize()) {
setDataSize(startFrom + newData.length);
}
replace(startFrom, newData);
return newData.length;
}
}
@Override
public void fillData(long startFrom, long length) {
fillData(startFrom, length, (byte) 0);
}
@Override
public void fillData(long startFrom, long length, byte fill) {
if (length > 0) {
try {
for (int i = (int) startFrom; i < startFrom + length; i++) {
data.put(i, fill);
}
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
}
}
@Override
public void replace(long targetPosition, BinaryData sourceData) {
replace(targetPosition, sourceData, 0, sourceData.getDataSize());
}
@Override
public void replace(long targetPosition, BinaryData replacingData, long startFrom, long replacingLength) {
if (targetPosition + replacingLength > getDataSize()) {
throw new OutOfBoundsException(WRONG_REPLACE_POSITION_ERROR);
}
while (replacingLength > 0) {
setByte(targetPosition, replacingData.getByte(startFrom));
targetPosition++;
startFrom++;
replacingLength--;
}
}
@Override
public void replace(long targetPosition, byte[] replacingData) {
replace(targetPosition, replacingData, 0, replacingData.length);
}
@Override
public void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length) {
if (targetPosition + length > getDataSize()) {
throw new OutOfBoundsException(WRONG_REPLACE_POSITION_ERROR);
}
try {
data.position((int) targetPosition);
data.put(replacingData, replacingDataOffset, length);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
}
@Override
public void remove(long startFrom, long length) {
if (startFrom + length > data.capacity()) {
throw new OutOfBoundsException("Cannot remove from " + startFrom + " with length " + length);
}
if (length > 0) {
ByteBuffer newData = allocateBuffer((int) (data.capacity() - length));
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
BufferEditableData.put(newData, (int) startFrom, data, (int) (startFrom + length), (int) (data.capacity() - startFrom - length));
data = newData;
}
}
@Nonnull
@Override
public BufferEditableData copy() {
ByteBuffer copy = allocateBuffer(data.capacity());
synchronized (this) {
data.rewind();
copy.put(data);
}
return new BufferEditableData(copy);
}
@Nonnull
@Override
public BufferEditableData copy(long startFrom, long length) {
if (startFrom + length > data.capacity()) {
throw new OutOfBoundsException("Attempt to copy outside of data");
}
ByteBuffer copy = allocateBuffer((int) length);
synchronized (this) {
BufferEditableData.put(copy, 0, data, (int) startFrom, (int) length);
return new BufferEditableData(copy);
}
}
@Override
public void clear() {
data = allocateBuffer(0);
}
@Override
public void loadFromStream(InputStream inputStream) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[BUFFER_SIZE];
int read;
do {
read = inputStream.read(buffer);
if (read > 0) {
output.write(buffer, 0, read);
}
} while (read > 0);
byte[] byteArray = output.toByteArray();
data = allocateBuffer(byteArray.length);
data.put(byteArray);
}
}
@Nonnull
@Override
public OutputStream getDataOutputStream() {
return new BinaryDataOutputStream(this);
}
public static void put(ByteBuffer target, int position, ByteBuffer source, int offset, int length) throws IndexOutOfBoundsException {
if (target == source) {
byte[] buffer = new byte[BUFFER_SIZE];
if (position < offset) {
while (length > 0) {
int blockSize = length > BUFFER_SIZE ? BUFFER_SIZE : length;
source.position(offset);
source.get(buffer, 0, blockSize);
target.position(position);
target.put(buffer, 0, blockSize);
offset += blockSize;
position += blockSize;
length -= blockSize;
}
} else if (position > offset) {
while (length > 0) {
int blockSize = length > BUFFER_SIZE ? BUFFER_SIZE : length;
source.position(offset + length - blockSize);
source.get(buffer, 0, blockSize);
target.position(position + length - blockSize);
target.put(buffer, 0, blockSize);
length -= blockSize;
}
}
target.clear();
return;
}
source.position(offset);
source.limit(offset + length);
target.position(position);
target.put(source);
target.clear();
source.clear();
}
}
@@ -0,0 +1,82 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.buffer;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Implementation of binary data interface using direct byte buffer.
*/
@ParametersAreNonnullByDefault
public class DirectBufferData extends BufferData {
public DirectBufferData() {
this((ByteBuffer) null);
}
/**
* Creates instance directly wrapping provided byte buffer.
*
* @param data byte buffer
*/
public DirectBufferData(@Nullable ByteBuffer data) {
super(data != null ? data : DirectBufferData.allocateBufferInt(0));
}
/**
* Creates instance setting value to provided byte array.
*
* @param data byte array
*/
public DirectBufferData(@Nullable byte[] data) {
super(DirectBufferData.allocateBufferInt(data));
}
/**
* Creates instance with specified size.
*
* @param dataSize data size
*/
public DirectBufferData(int dataSize) {
super(DirectBufferData.allocateBufferInt(dataSize));
}
@Nonnull
@Override
protected ByteBuffer allocateBuffer(int capacity) {
return DirectBufferData.allocateBufferInt(capacity);
}
@Nonnull
private static ByteBuffer allocateBufferInt(int capacity) {
return ByteBuffer.allocateDirect(capacity);
}
@Nonnull
private static ByteBuffer allocateBufferInt(@Nullable byte[] data) {
if (data == null) {
return DirectBufferData.allocateBufferInt(0);
} else {
ByteBuffer buffer = DirectBufferData.allocateBufferInt(data.length);
buffer.put(data);
buffer.clear();
return buffer;
}
}
}
@@ -0,0 +1,82 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.buffer;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Implementation of editable binary data interface using direct byte buffer.
*/
@ParametersAreNonnullByDefault
public class DirectBufferEditableData extends BufferEditableData {
public DirectBufferEditableData() {
this((ByteBuffer) null);
}
/**
* Creates instance directly wrapping provided byte buffer.
*
* @param data byte buffer
*/
public DirectBufferEditableData(@Nullable ByteBuffer data) {
super(data != null ? data : DirectBufferEditableData.allocateBufferInt(0));
}
/**
* Creates instance setting value to provided byte array.
*
* @param data byte array
*/
public DirectBufferEditableData(@Nullable byte[] data) {
super(DirectBufferEditableData.allocateBufferInt(data));
}
/**
* Creates instance with specified size.
*
* @param dataSize data size
*/
public DirectBufferEditableData(int dataSize) {
super(DirectBufferEditableData.allocateBufferInt(dataSize));
}
@Nonnull
@Override
protected ByteBuffer allocateBuffer(int capacity) {
return DirectBufferEditableData.allocateBufferInt(capacity);
}
@Nonnull
private static ByteBuffer allocateBufferInt(int capacity) {
return ByteBuffer.allocateDirect(capacity);
}
@Nonnull
private static ByteBuffer allocateBufferInt(@Nullable byte[] data) {
if (data == null) {
return DirectBufferEditableData.allocateBufferInt(0);
} else {
ByteBuffer buffer = DirectBufferEditableData.allocateBufferInt(data.length);
buffer.put(data);
buffer.clear();
return buffer;
}
}
}
@@ -0,0 +1,732 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.buffer.paged;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.BinaryDataInputStream;
import org.exbin.auxiliary.binary_data.BinaryDataOutputStream;
import org.exbin.auxiliary.binary_data.buffer.BufferData;
import org.exbin.auxiliary.binary_data.buffer.BufferEditableData;
import org.exbin.auxiliary.binary_data.DataOverflowException;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
import org.exbin.auxiliary.binary_data.paged.PagedData;
import org.exbin.auxiliary.binary_data.paged.DataPageCreator;
/**
* Paged data stored using byte buffer.
*/
@ParametersAreNonnullByDefault
public class BufferPagedData implements PagedData {
public static final int DEFAULT_PAGE_SIZE = 4096;
public static final long MAX_DATA_SIZE = Long.MAX_VALUE;
protected int pageSize = DEFAULT_PAGE_SIZE;
@Nonnull
protected final List<BufferData> data = new ArrayList<>();
@Nullable
protected DataPageCreator dataPageCreator = null;
public BufferPagedData() {
}
public BufferPagedData(DataPageCreator dataPageCreator) {
this.dataPageCreator = dataPageCreator;
}
public BufferPagedData(int pageSize) {
if (pageSize < 1) {
throw new InvalidParameterException("Page size cannot be less than 1");
}
this.pageSize = pageSize;
}
@Override
public boolean isEmpty() {
return data.isEmpty();
}
@Override
public long getDataSize() {
return (data.size() > 1 ? (long) (data.size() - 1) * pageSize : 0) + (!data.isEmpty() ? data.get(data.size() - 1).getDataSize() : 0);
}
@Override
public void setDataSize(long size) {
if (size < 0) {
throw new InvalidParameterException("Size cannot be negative");
}
long dataSize = getDataSize();
if (size > dataSize) {
int lastPage = (int) (dataSize / pageSize);
int lastPageSize = (int) (dataSize % pageSize);
long remaining = size - dataSize;
// extend last page
if (lastPageSize > 0) {
BufferData page = getPage(lastPage);
int nextPageSize = remaining + lastPageSize > pageSize ? pageSize : (int) remaining + lastPageSize;
BufferData newPage = createNewPage(nextPageSize);
page.getData().rewind();
newPage.getData().put(page.getData());
setPage(lastPage, newPage);
remaining -= (nextPageSize - lastPageSize);
lastPage++;
}
while (remaining > 0) {
int nextPageSize = remaining > pageSize ? pageSize : (int) remaining;
data.add(createNewPage(nextPageSize));
remaining -= nextPageSize;
}
} else if (size < dataSize) {
int lastPage = (int) (size / pageSize);
int lastPageSize = (int) (size % pageSize);
// shrink last page
if (lastPageSize > 0) {
BufferData page = getPage(lastPage);
BufferData newPage = createNewPage(lastPageSize);
BufferPagedData.put(newPage.getData(), 0, page.getData(), 0, lastPageSize);
setPage(lastPage, newPage);
lastPage++;
}
for (int pageIndex = data.size() - 1; pageIndex >= lastPage; pageIndex--) {
data.remove(pageIndex);
}
}
}
@Override
public byte getByte(long position) {
BufferData page = getPage((int) (position / pageSize));
try {
return page.getByte(position % pageSize);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
}
@Override
public void setByte(long position, byte value) {
BufferData page = getPage((int) (position / pageSize));
try {
page.getData().put((int) (position % pageSize), value);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
}
@Override
public void insertUninitialized(long startFrom, long length) {
if (length < 0) {
throw new IllegalArgumentException("Length of inserted block must be non-negative");
}
if (startFrom < 0) {
throw new IllegalArgumentException("Position of inserted block must be non-negative");
}
long dataSize = getDataSize();
if (startFrom > dataSize) {
throw new OutOfBoundsException("Inserted block must be inside or directly after existing data");
}
if (length > MAX_DATA_SIZE - getDataSize()) {
throw new DataOverflowException("Maximum array size overflow");
}
if (length == 0) {
return;
}
long copyLength = dataSize - startFrom;
dataSize = dataSize + length;
setDataSize(dataSize);
long sourceEnd = dataSize - length;
long targetEnd = dataSize;
// Backward copy
while (copyLength > 0) {
BufferData sourcePage;
int sourceOffset = (int) (sourceEnd % pageSize);
if (sourceOffset == 0) {
sourcePage = getPage((int) ((sourceEnd - 1) / pageSize));
sourceOffset = (int) sourcePage.getDataSize();
} else {
sourcePage = getPage((int) (sourceEnd / pageSize));
}
BufferData targetPage;
int targetOffset = (int) (targetEnd % pageSize);
if (targetOffset == 0) {
targetPage = getPage((int) ((targetEnd - 1) / pageSize));
targetOffset = (int) targetPage.getDataSize();
} else {
targetPage = getPage((int) (targetEnd / pageSize));
}
int copySize = Math.min(sourceOffset, targetOffset);
if (copySize > copyLength) {
copySize = (int) copyLength;
}
BufferPagedData.put(targetPage.getData(), targetOffset - copySize, sourcePage.getData(), sourceOffset - copySize, copySize);
copyLength -= copySize;
sourceEnd -= copySize;
targetEnd -= copySize;
}
}
@Override
public void insert(long startFrom, long length) {
insertUninitialized(startFrom, length);
fillData(startFrom, length);
}
@Override
public void insert(long startFrom, BinaryData insertedData) {
long length = insertedData.getDataSize();
insertUninitialized(startFrom, length);
replace(startFrom, insertedData, 0, length);
}
@Override
public void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
insertUninitialized(startFrom, insertedDataLength);
replace(startFrom, insertedData, insertedDataOffset, insertedDataLength);
}
@Override
public void insert(long startFrom, byte[] insertedData) {
insert(startFrom, insertedData, 0, insertedData.length);
}
@Override
public void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength) {
if (insertedDataLength <= 0) {
return;
}
insertUninitialized(startFrom, insertedDataLength);
while (insertedDataLength > 0) {
BufferData targetPage = getPage((int) (startFrom / pageSize));
int targetOffset = (int) (startFrom % pageSize);
int blockLength = pageSize - targetOffset;
if (blockLength > insertedDataLength) {
blockLength = insertedDataLength;
}
try {
targetPage.getData().position(targetOffset);
targetPage.getData().put(insertedData, insertedDataOffset, blockLength);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
insertedDataOffset += blockLength;
insertedDataLength -= blockLength;
startFrom += blockLength;
}
}
@Override
public long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException {
if (maximumDataSize > MAX_DATA_SIZE - getDataSize()) {
throw new DataOverflowException("Maximum array size overflow");
}
if (startFrom > getDataSize()) {
setDataSize(startFrom);
}
long loadedData = 0;
int pageOffset = (int) (startFrom % pageSize);
byte[] buffer = new byte[pageSize];
while (maximumDataSize == -1 || maximumDataSize > 0) {
int dataToRead = pageSize - pageOffset;
if (maximumDataSize >= 0 && maximumDataSize < dataToRead) {
dataToRead = (int) maximumDataSize;
}
if (pageOffset > 0 && dataToRead > pageOffset) {
// Align to data pages
dataToRead = pageOffset;
}
int readLength = 0;
while (dataToRead > 0) {
int read = inputStream.read(buffer, readLength, dataToRead);
if (read == -1) {
break;
}
readLength += read;
dataToRead -= read;
}
insert(startFrom, buffer, 0, readLength);
startFrom += readLength;
if (maximumDataSize >= 0) {
maximumDataSize -= readLength;
}
loadedData += readLength;
pageOffset = 0;
}
return loadedData;
}
@Override
public void fillData(long startFrom, long length) {
fillData(startFrom, length, (byte) 0);
}
@Override
public void fillData(long startFrom, long length, byte fill) {
if (length < 0) {
throw new IllegalArgumentException("Length of filled block must be non-negative");
}
if (startFrom < 0) {
throw new IllegalArgumentException("Position of filler block must be non-negative");
}
if (startFrom + length > getDataSize()) {
throw new OutOfBoundsException("Filled block must be inside existing data");
}
while (length > 0) {
BufferData page = getPage((int) (startFrom / pageSize));
int pageOffset = (int) (startFrom % pageSize);
int fillSize = (int) (page.getDataSize() - pageOffset);
if (fillSize > length) {
fillSize = (int) length;
}
ByteBuffer pageBuffer = page.getData();
for (int i = pageOffset; i < pageOffset + fillSize; i++) {
pageBuffer.put(i, fill);
}
length -= fillSize;
startFrom += fillSize;
}
}
@Nonnull
@Override
public BufferPagedData copy() {
BufferPagedData targetData = new BufferPagedData();
targetData.insert(0, this);
return targetData;
}
@Nonnull
@Override
public BufferPagedData copy(long startFrom, long length) {
BufferPagedData targetData = new BufferPagedData();
targetData.insertUninitialized(0, length);
targetData.replace(0, this, startFrom, length);
return targetData;
}
@Override
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
while (length > 0) {
BufferData page = getPage((int) (startFrom / pageSize));
int pageOffset = (int) (startFrom % pageSize);
int copySize = pageSize - pageOffset;
if (copySize > length) {
copySize = length;
}
page.copyToArray(pageOffset, target, offset, copySize);
length -= copySize;
offset += copySize;
startFrom += copySize;
}
}
@Override
public void remove(long startFrom, long length) {
if (length < 0) {
throw new IllegalArgumentException("Length of removed block must be non-negative");
}
if (startFrom < 0) {
throw new IllegalArgumentException("Position of removed block must be non-negative");
}
if (startFrom + length > getDataSize()) {
throw new OutOfBoundsException("Removed block must be inside existing data");
}
if (length > 0) {
long dataSize = getDataSize();
replace(startFrom, this, startFrom + length, dataSize - startFrom - length);
setDataSize(dataSize - length);
}
}
@Override
public void clear() {
data.clear();
}
/**
* Returns number of pages currently used.
*
* @return count of pages
*/
@Override
public int getPagesCount() {
return data.size();
}
/**
* Returns currently used page size.
*
* @return page size in bytes
*/
@Override
public int getPageSize() {
return pageSize;
}
/**
* Gets data page allowing direct access to it.
*
* @param pageIndex page index
* @return data page
*/
@Nonnull
@Override
public BufferData getPage(int pageIndex) {
try {
return data.get(pageIndex);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
}
/**
* Sets data page replacing existing page by reference.
*
* @param pageIndex page index
* @param dataPage data page
*/
@Override
public void setPage(int pageIndex, BinaryData dataPage) {
if (!(dataPage instanceof BufferData)) {
throw new UnsupportedOperationException("Unsupported data page type");
}
try {
data.set(pageIndex, (BufferData) dataPage);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
}
@Override
public void replace(long targetPosition, BinaryData replacingData) {
replace(targetPosition, replacingData, 0, replacingData.getDataSize());
}
@Override
public void replace(long targetPosition, BinaryData replacingData, long startFrom, long length) {
if (targetPosition + length > getDataSize()) {
throw new OutOfBoundsException("Data can be replaced only inside or at the end");
}
if (replacingData instanceof BufferPagedData) {
if (replacingData != this || (startFrom > targetPosition) || (startFrom + length < targetPosition)) {
while (length > 0) {
BufferData page = getPage((int) (targetPosition / pageSize));
int offset = (int) (targetPosition % pageSize);
BufferData sourcePage = ((BufferPagedData) replacingData).getPage((int) (startFrom / ((BufferPagedData) replacingData).getPageSize()));
int sourcePageSize = (int) sourcePage.getDataSize();
int sourceOffset = (int) (startFrom % ((BufferPagedData) replacingData).getPageSize());
int copySize = pageSize - offset;
if (copySize > sourcePageSize - sourceOffset) {
copySize = sourcePageSize - sourceOffset;
}
if (copySize > length) {
copySize = (int) length;
}
try {
BufferPagedData.put(page.getData(), offset, sourcePage.getData(), sourceOffset, copySize);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
length -= copySize;
targetPosition += copySize;
startFrom += copySize;
}
} else {
targetPosition += length - 1;
startFrom += length - 1;
while (length > 0) {
BufferData page = getPage((int) (targetPosition / pageSize));
int upTo = (int) (targetPosition % pageSize) + 1;
BufferData sourcePage = ((BufferPagedData) replacingData).getPage((int) (startFrom / ((BufferPagedData) replacingData).getPageSize()));
int sourceUpTo = (int) (startFrom % ((BufferPagedData) replacingData).getPageSize()) + 1;
int copySize = upTo;
if (copySize > sourceUpTo) {
copySize = sourceUpTo;
}
if (copySize > length) {
copySize = (int) length;
}
int offset = upTo - copySize;
int sourceOffset = sourceUpTo - copySize;
BufferPagedData.put(page.getData(), offset, sourcePage.getData(), sourceOffset, copySize);
length -= copySize;
targetPosition -= copySize;
startFrom -= copySize;
}
}
} else {
while (length > 0) {
BufferData page = getPage((int) (targetPosition / pageSize));
int offset = (int) (targetPosition % pageSize);
int copySize = pageSize - offset;
if (copySize > length) {
copySize = (int) length;
}
byte[] buffer = new byte[copySize];
replacingData.copyToArray(startFrom, buffer, 0, copySize);
page.getData().position(offset);
page.getData().put(buffer);
length -= copySize;
targetPosition += copySize;
startFrom += copySize;
}
}
}
@Override
public void replace(long targetPosition, byte[] replacingData) {
replace(targetPosition, replacingData, 0, replacingData.length);
}
@Override
public void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length) {
if (targetPosition + length > getDataSize()) {
throw new OutOfBoundsException("Data can be replaced only inside or at the end");
}
while (length > 0) {
BufferData page = getPage((int) (targetPosition / pageSize));
int offset = (int) (targetPosition % pageSize);
int copySize = pageSize - offset;
if (copySize > length) {
copySize = length;
}
try {
page.getData().position(offset);
page.getData().put(replacingData, replacingDataOffset, copySize);
} catch (IndexOutOfBoundsException ex) {
throw new OutOfBoundsException(ex);
}
length -= copySize;
targetPosition += copySize;
replacingDataOffset += copySize;
}
}
@Override
public void loadFromStream(InputStream inputStream) throws IOException {
data.clear();
byte[] buffer = new byte[pageSize];
int cnt;
int offset = 0;
while ((cnt = inputStream.read(buffer, offset, buffer.length - offset)) > 0) {
if (cnt + offset < pageSize) {
offset = offset + cnt;
} else {
data.add(createNewPage(buffer));
buffer = new byte[pageSize];
offset = 0;
}
}
if (offset > 0) {
byte[] tail = new byte[offset];
System.arraycopy(buffer, 0, tail, 0, offset);
data.add(createNewPage(tail));
}
}
@Override
public void saveToStream(OutputStream outputStream) throws IOException {
for (BufferData dataPage : data) {
dataPage.saveToStream(outputStream);
}
}
@Nonnull
@Override
public OutputStream getDataOutputStream() {
return new BinaryDataOutputStream(this);
}
@Nonnull
@Override
public InputStream getDataInputStream() {
return new BinaryDataInputStream(this);
}
@Nonnull
protected BufferData createNewPage(byte[] pageData) {
if (dataPageCreator != null) {
EditableBinaryData page = dataPageCreator.createPage(pageData.length);
page.replace(0, pageData);
return (BufferData) page;
}
return new BufferData(pageData);
}
@Nonnull
protected BufferData createNewPage(int pageDataSize) {
if (dataPageCreator != null) {
return (BufferData) dataPageCreator.createPage(pageDataSize);
}
return new BufferData(pageDataSize);
}
@Nullable
public DataPageCreator getDataPageCreator() {
return dataPageCreator;
}
public void setDataPageCreator(@Nullable DataPageCreator dataPageCreator) {
this.dataPageCreator = dataPageCreator;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
if (obj instanceof BinaryData) {
BinaryData other = (BinaryData) obj;
long dataSize = getDataSize();
if (other.getDataSize() != dataSize) {
return false;
}
int pageIndex = 0;
int bufferSize = dataSize > pageSize ? pageSize : (int) dataSize;
byte[] buffer = new byte[bufferSize];
int offset = 0;
int remain = (int) dataSize;
while (remain > 0) {
int length = remain > bufferSize ? bufferSize : remain;
other.copyToArray(offset, buffer, 0, length);
BufferData pageData = data.get(pageIndex);
for (int i = 0; i < length; i++) {
if (pageData.getByte(i) != buffer[i]) {
return false;
}
}
offset += length;
remain -= length;
pageIndex++;
}
return true;
}
return false;
}
final BufferPagedData other = (BufferPagedData) obj;
long dataSize = getDataSize();
if (other.getDataSize() != dataSize) {
return false;
}
int pageIndex = 0;
int otherPageIndex = 0;
long offset = 0;
long remain = dataSize;
while (remain > 0) {
int pageOffset = (int) (offset % pageSize);
int otherPageOffset = (int) (offset % other.pageSize);
int length = remain > pageSize - pageOffset ? pageSize - pageOffset : (int) remain;
if (length > other.pageSize - otherPageOffset) {
length = other.pageSize - otherPageOffset;
}
BufferData pageData = data.get(pageIndex);
BufferData otherPageData = other.data.get(otherPageIndex);
int pageTestPos = pageOffset;
int otherPageTestPos = otherPageOffset;
for (int i = 0; i < length; i++) {
if (pageData.getByte(pageTestPos) != otherPageData.getByte(otherPageTestPos)) {
return false;
}
pageTestPos++;
otherPageTestPos++;
}
offset += length;
remain -= length;
pageIndex++;
}
return true;
}
@Override
public int hashCode() {
return Objects.hash(getDataSize());
}
@Override
public void dispose() {
}
private static void put(ByteBuffer target, int position, ByteBuffer source, int offset, int length) throws IndexOutOfBoundsException {
BufferEditableData.put(target, position, source, offset, length);
}
}
@@ -0,0 +1,80 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.delta.list.DoublyLinkedItem;
/**
* Abstract data segment of delta data source.
*/
@ParametersAreNonnullByDefault
public abstract class DataSegment implements DoublyLinkedItem<DataSegment> {
@Nullable
private DataSegment previous;
@Nullable
private DataSegment next;
public DataSegment() {
}
/**
* Returns start position.
*
* @return start position
*/
public abstract long getStartPosition();
/**
* Returns length of this segment in bytes.
*
* @return length of this segment
*/
public abstract long getLength();
/**
* Returns detached copy of this segment.
*
* @return copy of this segment
*/
@Nonnull
public abstract DataSegment copy();
@Nullable
@Override
public DataSegment getNext() {
return next;
}
@Override
public void setNext(@Nullable DataSegment next) {
this.next = next;
}
@Nullable
@Override
public DataSegment getPrev() {
return previous;
}
@Override
public void setPrev(@Nullable DataSegment previous) {
this.previous = previous;
}
}
@@ -0,0 +1,95 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import java.io.IOException;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Data source interface.
*/
@ParametersAreNonnullByDefault
public interface DataSource {
/**
* Returns length of the data.
*
* @return length of the data
* @throws java.io.IOException input output exception
*/
long getDataLength() throws IOException;
/**
* Sets data length.
*
* @param dataLength data length
* @throws java.io.IOException input output exception
*/
void setDataLength(long dataLength) throws IOException;
/**
* Returns single byte of the data.
*
* @param position data position
* @return byte value
* @throws java.io.IOException input output exception
*/
byte getByte(long position) throws IOException;
/**
* Sets single byte of the data.
*
* @param position data position
* @param value byte value
* @throws java.io.IOException input output exception
*/
void setByte(long position, byte value) throws IOException;
/**
* Reads data to buffer.
*
* @param position data position
* @param buffer data buffer
* @param offset buffer offset
* @param length data length
* @return length of data red
* @throws java.io.IOException input output exception
*/
int read(long position, byte[] buffer, int offset, int length) throws IOException;
/**
* Writes data from buffer.
*
* @param position data position
* @param buffer data buffer
* @param offset buffer offset
* @param length data length
* @throws java.io.IOException input output exception
*/
void write(long position, byte[] buffer, int offset, int length) throws IOException;
/**
* Clears caches.
*/
void clearCache();
/**
* Close data source.
*
* @throws java.io.IOException input output exception
*/
void close() throws IOException;
}
@@ -0,0 +1,393 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
import org.exbin.auxiliary.binary_data.delta.list.DefaultDoublyLinkedList;
/**
* Delta document defined as a sequence of segments.
*/
@ParametersAreNonnullByDefault
public class DeltaDocument implements EditableBinaryData {
private final SegmentsRepository repository;
private DataSource dataSource;
private final DefaultDoublyLinkedList<DataSegment> segments = new DefaultDoublyLinkedList<>();
private long dataLength = 0;
private final DeltaDocumentWindow pointerWindow;
private final List<DeltaDocumentChangedListener> changeListeners = new ArrayList<>();
private static final int BUFFER_SIZE = 4096;
public DeltaDocument(SegmentsRepository repository, DataSource dataSource) throws IOException {
this.repository = repository;
this.dataSource = dataSource;
dataLength = dataSource.getDataLength();
if (dataLength > 0) {
DataSegment fullFileSegment = repository.createSourceSegment(dataSource, 0, dataLength);
segments.add(fullFileSegment);
}
pointerWindow = new DeltaDocumentWindow(this);
pointerWindow.reset();
}
public DeltaDocument(SegmentsRepository repository) {
this.repository = repository;
dataLength = 0;
pointerWindow = new DeltaDocumentWindow(this);
pointerWindow.reset();
}
/**
* Method for accessing data pages.
* <p>
* Use only if you know what you are doing.
*
* @return segments
*/
@Nonnull
public DefaultDoublyLinkedList<DataSegment> getSegments() {
return segments;
}
/**
* Returns segment starting at or before given position and ending after it.
* <p>
* Returns null if position is at the end or after then end of the document.
*
* @param position requested position
* @return data segment or null
*/
@Nullable
public DataSegment getSegment(long position) {
return pointerWindow.getSegment(position);
}
@Override
public synchronized boolean isEmpty() {
return dataLength == 0;
}
@Override
public synchronized long getDataSize() {
return dataLength;
}
@Override
public synchronized byte getByte(long position) {
return pointerWindow.getByte(position);
}
@Override
public synchronized void setByte(long position, byte value) {
pointerWindow.setByte(position, value);
}
@Override
public synchronized void insertUninitialized(long startFrom, long length) {
pointerWindow.insertUninitialized(startFrom, length);
}
@Override
public synchronized void insert(long startFrom, long length) {
pointerWindow.insert(startFrom, length);
}
@Override
public synchronized void insert(long startFrom, byte[] insertedData) {
pointerWindow.insert(startFrom, insertedData);
}
@Override
public synchronized void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength) {
pointerWindow.insert(startFrom, insertedData, insertedDataOffset, insertedDataLength);
}
@Override
public synchronized void insert(long startFrom, BinaryData insertedData) {
pointerWindow.insert(startFrom, insertedData);
}
@Override
public synchronized void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
pointerWindow.insert(startFrom, insertedData, insertedDataOffset, insertedDataLength);
}
/**
* Directly inserts segment into given position.
*
* @param startFrom start position
* @param segment inserted segment
*/
public synchronized void insertSegment(long startFrom, DataSegment segment) {
pointerWindow.insertSegment(startFrom, segment);
}
@Override
public synchronized long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException {
// TODO optimization later
long processed = 0;
byte[] buffer = new byte[BUFFER_SIZE];
while (maximumDataSize == -1 || maximumDataSize > 0) {
int toRead = BUFFER_SIZE;
if (maximumDataSize >= 0 && maximumDataSize < toRead) {
toRead = (int) maximumDataSize;
}
int read = inputStream.read(buffer, 0, toRead);
if (read == -1) {
break;
}
pointerWindow.insert(startFrom, buffer, 0, read);
if (maximumDataSize >= 0) {
maximumDataSize -= read;
}
startFrom += read;
processed += read;
}
return processed;
}
@Override
public synchronized void replace(long targetPosition, BinaryData replacingData) {
remove(targetPosition, replacingData.getDataSize());
insert(targetPosition, replacingData);
}
@Override
public synchronized void replace(long targetPosition, BinaryData replacingData, long startFrom, long length) {
remove(targetPosition, length);
insert(targetPosition, replacingData, startFrom, length);
}
@Override
public synchronized void replace(long targetPosition, byte[] replacingData) {
remove(targetPosition, replacingData.length);
insert(targetPosition, replacingData);
}
@Override
public synchronized void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length) {
remove(targetPosition, length);
insert(targetPosition, replacingData, replacingDataOffset, length);
}
/**
* Directly replaces segment into given position.
*
* @param targetPosition target position
* @param segment inserted segment
*/
public synchronized void replaceSegment(long targetPosition, DataSegment segment) {
remove(targetPosition, segment.getLength());
insertSegment(targetPosition, segment);
}
@Override
public synchronized void fillData(long startFrom, long length) {
fillData(startFrom, length, (byte) 0);
}
@Override
public synchronized void fillData(long startFrom, long length, byte fill) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public synchronized void remove(long startFrom, long length) {
pointerWindow.remove(startFrom, length);
}
@Override
public synchronized void clear() {
dataLength = 0;
segments.clear();
pointerWindow.reset();
}
@Override
public synchronized void dispose() {
repository.dropDocument(this);
}
@Override
public synchronized void loadFromStream(InputStream stream) throws IOException {
clear();
DeltaDocumentWindow documentWindow = new DeltaDocumentWindow(this);
byte[] buffer = new byte[BUFFER_SIZE];
long position = 0;
int read;
do {
read = stream.read(buffer);
if (read > 0) {
documentWindow.insert(position, buffer, 0, read);
position += read;
}
} while (read >= 0);
}
@Override
public synchronized void saveToStream(OutputStream stream) throws IOException {
DeltaDocumentWindow documentWindow = new DeltaDocumentWindow(this);
byte[] buffer = new byte[BUFFER_SIZE];
long position = 0;
long dataSize = getDataSize();
while (position < dataSize) {
long remains = dataSize - position;
int toProcess = remains < BUFFER_SIZE ? (int) remains : BUFFER_SIZE;
documentWindow.copyToArray(position, buffer, 0, toProcess);
stream.write(buffer, 0, toProcess);
position += toProcess;
}
}
@Nonnull
@Override
public synchronized BinaryData copy() {
return pointerWindow.copy();
}
@Nonnull
@Override
public synchronized BinaryData copy(long startFrom, long length) {
return pointerWindow.copy(startFrom, length);
}
@Override
public synchronized void copyToArray(long startFrom, byte[] target, int offset, int length) {
// TODO optimization later
for (int i = 0; i < length; i++) {
target[offset + i] = getByte(startFrom + i);
}
}
@Nonnull
@Override
public synchronized OutputStream getDataOutputStream() {
return new DeltaDocumentOutputStream(this);
}
@Nonnull
@Override
public synchronized InputStream getDataInputStream() {
return new DeltaDocumentInputStream(this);
}
@Override
public synchronized void setDataSize(long dataSize) {
if (dataSize < dataLength) {
remove(dataSize, dataLength - dataSize);
} else if (dataSize > dataLength) {
insert(dataLength, dataSize - dataLength);
}
}
/**
* Performs save to source file.
*
* @throws java.io.IOException on input/output error
*/
public synchronized void save() throws IOException {
repository.saveDocument(this);
}
/**
* Resets cached state - needed after change.
*/
public synchronized void clearCache() {
pointerWindow.reset();
}
/* package */ void setDataLength(long dataSize) {
this.dataLength = dataSize;
}
/**
* Returns segment starting from given position or copy of part of the
* segment starting from given position up to the end of length.
*
* @param position position
* @param length length
* @return data segment
*/
@Nullable
public synchronized DataSegment getPartCopy(long position, long length) {
return pointerWindow.getPartCopy(position, length);
}
@Nullable
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Nonnull
public SegmentsRepository getRepository() {
return repository;
}
public void addChangeListener(DeltaDocumentChangedListener listener) {
changeListeners.add(listener);
}
public void removeChangeListener(DeltaDocumentChangedListener listener) {
changeListeners.remove(listener);
}
public void notifyChangeListeners(DeltaDocumentWindow window) {
for (DeltaDocumentChangedListener listener : changeListeners) {
listener.dataChanged(window);
}
}
public void validatePointerPosition() {
pointerWindow.validatePointerPosition();
}
public void validateDocumentSize() {
long segmentsSizeSum = 0;
DataSegment segment = segments.first();
while (segment != null) {
segmentsSizeSum += segment.getLength();
segment = segment.getNext();
}
if (segmentsSizeSum != getDataSize()) {
throw new IllegalStateException("Invalid size " + getDataSize() + " (expected " + segmentsSizeSum + ")");
}
}
public void validate() {
validatePointerPosition();
validateDocumentSize();
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Listener for delta document data changes.
*/
@ParametersAreNonnullByDefault
public interface DeltaDocumentChangedListener {
/**
* Change in data occurred.
*
* @param window updated window for reference
*/
void dataChanged(DeltaDocumentWindow window);
}
@@ -0,0 +1,125 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.SeekableStream;
/**
* Delta document input stream.
* <p>
* Can process expanding document.
*/
@ParametersAreNonnullByDefault
public class DeltaDocumentInputStream extends InputStream implements SeekableStream {
@Nonnull
private final DeltaDocumentWindow data;
private long position = 0;
private long mark = 0;
public DeltaDocumentInputStream(DeltaDocument document) {
this.data = new DeltaDocumentWindow(document);
}
@Override
public int read() throws IOException {
if (position >= data.getDataSize()) {
return -1;
}
try {
return data.getByte(position++) & 0xFF;
} catch (ArrayIndexOutOfBoundsException ex) {
return -1;
}
}
@Override
public int read(byte[] output, int offset, int length) throws IOException {
if (output.length == 0 || length == 0) {
return 0;
}
long dataSize = data.getDataSize();
if (position >= dataSize) {
return -1;
}
if (position + length > dataSize) {
length = (int) (dataSize - position);
}
data.copyToArray(position, output, offset, length);
position += length;
return length;
}
@Override
public void close() throws IOException {
position = data.getDataSize();
}
@Override
public int available() throws IOException {
return (int) (data.getDataSize() - position);
}
@Override
public void seek(long position) throws IOException {
this.position = position;
}
public long getProcessedSize() {
return position;
}
@Override
public long getStreamSize() {
return -1;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void reset() throws IOException {
position = mark;
}
@Override
public synchronized void mark(int readlimit) {
mark = position;
}
@Override
public long skip(long n) throws IOException {
long dataSize = data.getDataSize();
if (position + n < dataSize) {
position += n;
return n;
}
long skipped = dataSize - position;
position = dataSize;
return skipped;
}
}
@@ -0,0 +1,88 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
import org.exbin.auxiliary.binary_data.SeekableStream;
/**
* Delta document output stream.
* <p>
* Data are expanded as needed.
*/
@ParametersAreNonnullByDefault
public class DeltaDocumentOutputStream extends OutputStream implements SeekableStream {
@Nonnull
private final DeltaDocumentWindow data;
private long position = 0;
public DeltaDocumentOutputStream(DeltaDocument document) {
this.data = new DeltaDocumentWindow(document);
}
@Override
public void write(int value) throws IOException {
long dataSize = data.getDataSize();
if (position == dataSize) {
dataSize++;
data.setDataSize(dataSize);
}
data.setByte(position++, (byte) value);
}
@Override
public void write(byte[] input, int offset, int length) throws IOException {
if (length == 0) {
return;
}
data.insert(position, input, offset, length);
position += length;
}
@Override
public void seek(long position) throws IOException {
if (position < 0) {
throw new OutOfBoundsException("Position is outside of available range");
}
if (position > data.getDataSize()) {
data.setDataSize(position);
}
this.position = position;
}
@Override
public long getStreamSize() {
return -1;
}
public long getProcessedSize() {
return position;
}
@Override
public void close() throws IOException {
position = data.getDataSize();
}
}
@@ -0,0 +1,156 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.FinishableStream;
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
import org.exbin.auxiliary.binary_data.SeekableStream;
/**
* Delta document input stream.
*/
@ParametersAreNonnullByDefault
public class DeltaDocumentRangeInputStream extends InputStream implements SeekableStream, FinishableStream {
@Nonnull
private final DeltaDocumentWindow data;
protected final long startPosition;
protected final long length;
protected long position = 0;
private long mark = 0;
public DeltaDocumentRangeInputStream(DeltaDocument document) {
this.data = new DeltaDocumentWindow(document);
this.startPosition = 0;
this.position = 0;
this.length = document.getDataSize();
}
public DeltaDocumentRangeInputStream(DeltaDocument document, long startPosition, long length) {
if (startPosition < 0) {
throw new IllegalArgumentException("Negative position not allowed");
}
if (length < 0) {
throw new IllegalArgumentException("Negative length not allowed");
}
long sourceDataSize = document.getDataSize();
if (startPosition + length > sourceDataSize) {
throw new OutOfBoundsException("Target area is outside of available data");
}
this.data = new DeltaDocumentWindow(document);
this.startPosition = startPosition;
this.position = startPosition;
this.length = length;
}
@Override
public int read() throws IOException {
if (position > startPosition + length) {
return -1;
}
try {
return data.getByte(position++) & 0xFF;
} catch (ArrayIndexOutOfBoundsException ex) {
return -1;
}
}
@Override
public int read(byte[] output, int offset, int len) throws IOException {
if (output.length == 0 || len == 0) {
return 0;
}
if (position > startPosition + length - len) {
if (position >= startPosition + length) {
return -1;
}
len = (int) (startPosition + length - position);
}
data.copyToArray(position, output, offset, len);
position += len;
return len;
}
@Override
public void close() throws IOException {
finish();
}
@Override
public int available() throws IOException {
return (int) ((startPosition + length) - position);
}
@Override
public void seek(long position) throws IOException {
if (position < 0 || position > length) {
throw new OutOfBoundsException("Position is outside of available range");
}
this.position = startPosition + position;
}
@Override
public long finish() throws IOException {
position = startPosition + length;
return length;
}
@Override
public long getProcessedSize() {
return position - startPosition;
}
@Override
public long getStreamSize() {
return length;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void reset() throws IOException {
position = mark;
}
@Override
public synchronized void mark(int readlimit) {
mark = position;
}
@Override
public long skip(long n) throws IOException {
if (position - startPosition + n < length) {
position += n;
return n;
}
long skipped = startPosition + length - position;
position = startPosition + length;
return skipped;
}
}
@@ -0,0 +1,115 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.FinishableStream;
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
import org.exbin.auxiliary.binary_data.SeekableStream;
/**
* Delta document output stream.
*/
@ParametersAreNonnullByDefault
public class DeltaDocumentRangeOutputStream extends OutputStream implements SeekableStream, FinishableStream {
@Nonnull
private final DeltaDocumentWindow data;
protected final long startPosition;
protected final long length;
protected long position = 0;
public DeltaDocumentRangeOutputStream(DeltaDocument document) {
this.data = new DeltaDocumentWindow(document);
this.startPosition = 0;
this.length = document.getDataSize();
}
public DeltaDocumentRangeOutputStream(DeltaDocument document, long startPosition, long length) {
if (startPosition < 0) {
throw new IllegalArgumentException("Negative position not allowed");
}
if (length < 0) {
throw new IllegalArgumentException("Negative length not allowed");
}
long sourceDataSize = document.getDataSize();
if (startPosition + length > sourceDataSize) {
throw new OutOfBoundsException("Target area is outside of available data");
}
this.data = new DeltaDocumentWindow(document);
this.startPosition = startPosition;
this.position = startPosition;
this.length = length;
}
@Override
public void write(int value) throws IOException {
if (position >= startPosition + length) {
throw new IOException("Position is outside of available range");
}
data.setByte(position++, (byte) value);
}
@Override
public void write(byte[] input, int offset, int len) throws IOException {
if (len == 0) {
return;
}
if (position + len > startPosition + length) {
throw new OutOfBoundsException("Target area is outside of available data");
}
// TODO replace
data.remove(position, len);
data.insert(position, input, offset, len);
position += len;
}
@Override
public void seek(long position) throws IOException {
if (position < 0 || position > length) {
throw new OutOfBoundsException("Position is outside of available range");
}
this.position = position;
}
@Override
public long getStreamSize() {
return length;
}
@Override
public long getProcessedSize() {
return position - startPosition;
}
@Override
public void close() throws IOException {
finish();
}
@Override
public long finish() throws IOException {
position = startPosition + length;
return length;
}
}
@@ -0,0 +1,723 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
import org.exbin.auxiliary.binary_data.delta.list.DefaultDoublyLinkedList;
/**
* Access window for delta document.
*/
@ParametersAreNonnullByDefault
public class DeltaDocumentWindow {
@Nonnull
private final DeltaDocument document;
private final DataPointer pointer = new DataPointer();
public DeltaDocumentWindow(DeltaDocument document) {
this.document = document;
document.addChangeListener((DeltaDocumentWindow window) -> {
if (window != DeltaDocumentWindow.this) {
pointer.segment = window.pointer.segment;
pointer.position = window.pointer.position;
}
});
}
public long getDataSize() {
return document.getDataSize();
}
public byte getByte(long position) {
focusSegment(position);
DataSegment targetSegment = pointer.segment;
if (targetSegment == null) {
throw new NullPointerException("Missing segment for position " + position);
}
if (targetSegment instanceof SourceSegment) {
return ((SourceSegment) targetSegment).getByte(targetSegment.getStartPosition() + (position - pointer.position));
} else {
return ((MemorySegment) targetSegment).getByte(targetSegment.getStartPosition() + (position - pointer.position));
}
}
public void setByte(long position, byte value) {
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
focusSegment(position);
if (pointer.segment instanceof SourceSegment) {
if (pointer.position != position) {
splitSegment(position);
focusSegment(position);
}
DataSegment prev = segments.prevTo(pointer.segment);
if (prev instanceof MemorySegment) {
repository.setMemoryByte((MemorySegment) prev, prev.getLength(), value);
} else {
MemorySegment segment = repository.createMemorySegment(repository.openMemorySource(), 0, 0);
repository.setMemoryByte(segment, 0, value);
segments.addBefore(pointer.segment, segment);
}
pointer.position++;
SourceSegment documentSegment = ((SourceSegment) pointer.segment);
if (documentSegment.getLength() == 1) {
segments.remove(documentSegment);
repository.dropSegment(documentSegment);
pointer.position = 0;
pointer.segment = null;
} else {
repository.updateSegment(documentSegment, documentSegment.getStartPosition() + 1, documentSegment.getLength() - 1);
}
} else {
if (pointer.segment == null) {
pointer.segment = repository.createMemorySegment(repository.openMemorySource(), 0, 0);
segments.add(pointer.segment);
}
repository.setMemoryByte((MemorySegment) pointer.segment, position - pointer.position, value);
}
if (position >= getDataSize()) {
document.setDataLength(position + 1);
}
document.notifyChangeListeners(this);
}
public void insertUninitialized(long startFrom, long length) {
if (length == 0) {
return;
}
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
focusSegment(startFrom);
long targetLength = document.getDataSize() + length;
if (pointer.segment instanceof MemorySegment) {
repository.insertUninitializedMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, length);
document.setDataLength(targetLength);
} else if (pointer.segment != null && pointer.position == startFrom && pointer.segment.getPrev() instanceof MemorySegment
&& pointer.segment.getPrev().getStartPosition() + pointer.segment.getPrev().getLength() == ((MemorySegment) pointer.segment.getPrev()).getSource().getDataSize()) {
MemorySegment prevSegment = (MemorySegment) pointer.segment.getPrev();
prevSegment.getSource().insertUninitialized(prevSegment.getSource().getDataSize(), length);
repository.updateSegmentLength(prevSegment, prevSegment.getLength() + length);
pointer.position++;
document.setDataLength(targetLength);
} else if (pointer.segment == null && segments.last() instanceof MemorySegment
&& segments.last().getStartPosition() + segments.last().getLength() == ((MemorySegment) segments.last()).getSource().getDataSize()) {
MemorySegment prevSegment = (MemorySegment) segments.last();
prevSegment.getSource().insertUninitialized(prevSegment.getSource().getDataSize(), length);
repository.updateSegmentLength(prevSegment, prevSegment.getLength() + length);
pointer.position++;
document.setDataLength(targetLength);
} else {
if (startFrom > pointer.position) {
splitSegment(startFrom);
focusSegment(startFrom);
}
MemorySegment insertedSegment = repository.createMemorySegment();
repository.insertUninitializedMemoryData(insertedSegment, 0, length);
if (pointer.segment == null) {
segments.add(insertedSegment);
} else {
segments.addBefore(pointer.segment, insertedSegment);
}
pointer.segment = insertedSegment;
document.setDataLength(targetLength);
tryMergeArea(startFrom, length);
}
document.notifyChangeListeners(this);
}
public void insert(long startFrom, long length) {
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
if (length == 0) {
return;
}
focusSegment(startFrom);
long targetLength = document.getDataSize() + length;
if (pointer.segment instanceof MemorySegment) {
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, length);
document.setDataLength(targetLength);
} else if (pointer.segment != null && pointer.position == startFrom && pointer.segment.getPrev() instanceof MemorySegment
&& pointer.segment.getPrev().getStartPosition() + pointer.segment.getPrev().getLength() == ((MemorySegment) pointer.segment.getPrev()).getSource().getDataSize()) {
MemorySegment prevSegment = (MemorySegment) pointer.segment.getPrev();
prevSegment.getSource().insert(prevSegment.getSource().getDataSize(), length);
repository.updateSegmentLength(prevSegment, prevSegment.getLength() + length);
pointer.position++;
document.setDataLength(targetLength);
} else if (pointer.segment == null && segments.last() instanceof MemorySegment
&& segments.last().getStartPosition() + segments.last().getLength() == ((MemorySegment) segments.last()).getSource().getDataSize()) {
MemorySegment prevSegment = (MemorySegment) segments.last();
prevSegment.getSource().insert(prevSegment.getSource().getDataSize(), length);
repository.updateSegmentLength(prevSegment, prevSegment.getLength() + length);
pointer.position++;
document.setDataLength(targetLength);
} else {
if (startFrom > pointer.position) {
splitSegment(startFrom);
focusSegment(startFrom);
}
MemorySegment insertedSegment = repository.createMemorySegment();
repository.insertMemoryData(insertedSegment, 0, length);
if (pointer.segment == null) {
segments.add(insertedSegment);
} else {
segments.addBefore(pointer.segment, insertedSegment);
}
pointer.segment = insertedSegment;
document.setDataLength(targetLength);
tryMergeArea(startFrom, length);
}
document.notifyChangeListeners(this);
}
public void insert(long startFrom, byte[] insertedData) {
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
if (insertedData.length == 0) {
return;
}
focusSegment(startFrom);
long targetLength = document.getDataSize() + insertedData.length;
if (pointer.segment instanceof MemorySegment) {
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, insertedData);
document.setDataLength(targetLength);
} else {
if (startFrom > pointer.position) {
splitSegment(startFrom);
focusSegment(startFrom);
}
MemorySegment insertedSegment = repository.createMemorySegment();
repository.insertMemoryData(insertedSegment, 0, insertedData);
if (pointer.segment == null) {
segments.add(insertedSegment);
} else {
segments.addBefore(pointer.segment, insertedSegment);
}
pointer.segment = insertedSegment;
document.setDataLength(targetLength);
tryMergeArea(startFrom, insertedData.length);
}
document.notifyChangeListeners(this);
}
public void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength) {
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
focusSegment(startFrom);
long targetLength = document.getDataSize() + insertedDataLength;
if (pointer.segment instanceof MemorySegment) {
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, insertedData);
document.setDataLength(targetLength);
} else {
if (startFrom > pointer.position) {
splitSegment(startFrom);
focusSegment(startFrom);
}
MemorySegment insertedSegment = repository.createMemorySegment();
repository.insertMemoryData(insertedSegment, 0, insertedData);
if (pointer.segment == null) {
segments.add(insertedSegment);
} else {
segments.addBefore(pointer.segment, insertedSegment);
}
pointer.segment = insertedSegment;
document.setDataLength(targetLength);
tryMergeArea(startFrom, insertedData.length);
}
document.notifyChangeListeners(this);
}
public void insert(long startFrom, BinaryData insertedData) {
if (insertedData.isEmpty()) {
return;
}
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
focusSegment(startFrom);
long targetLength = document.getDataSize() + insertedData.getDataSize();
if (insertedData instanceof DeltaDocument) {
if (pointer.position < startFrom) {
splitSegment(startFrom);
focusSegment(startFrom);
}
// Copy all segments from inserted document
DeltaDocument insertedDocument = (DeltaDocument) insertedData;
DataSegment segment = insertedDocument.getSegments().first();
DataSegment copy = repository.copySegment(segment);
DataSegment first = copy;
if (pointer.segment == null) {
segments.add(copy);
} else {
segments.addBefore(pointer.segment, copy);
}
DataSegment next = segment.getNext();
while (next != null) {
DataSegment nextCopy = repository.copySegment(next);
segments.addAfter(copy, nextCopy);
copy = nextCopy;
next = next.getNext();
}
pointer.segment = first;
document.setDataLength(targetLength);
tryMergeArea(startFrom, insertedData.getDataSize());
} else if (pointer.segment instanceof MemorySegment) {
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, insertedData);
document.setDataLength(targetLength);
} else {
if (pointer.position < startFrom) {
splitSegment(startFrom);
focusSegment(startFrom);
}
MemorySegment insertedSegment = repository.createMemorySegment();
repository.insertMemoryData(insertedSegment, 0, insertedData);
if (pointer.segment == null) {
segments.add(insertedSegment);
} else {
segments.addBefore(pointer.segment, insertedSegment);
}
pointer.segment = insertedSegment;
document.setDataLength(targetLength);
tryMergeArea(startFrom, insertedData.getDataSize());
}
document.notifyChangeListeners(this);
}
public void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
if (insertedDataLength == 0) {
return;
}
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
focusSegment(startFrom);
long targetLength = document.getDataSize() + insertedDataLength;
if (insertedData instanceof DeltaDocument) {
if (pointer.position < startFrom) {
splitSegment(startFrom);
focusSegment(startFrom);
}
// Copy all segments from inserted document
DeltaDocument insertedDocument = (DeltaDocument) insertedData;
long position = insertedDataOffset;
long length = insertedDataLength;
DataSegment segment = insertedDocument.getPartCopy(position, length);
position += segment.getLength();
length -= segment.getLength();
DataSegment first = segment;
if (pointer.segment == null) {
segments.add(segment);
} else {
segments.addBefore(pointer.segment, segment);
}
DataSegment next = segment.getNext();
while (length > 0) {
DataSegment nextSegment = insertedDocument.getPartCopy(position, length);
position += nextSegment.getLength();
length -= nextSegment.getLength();
segments.addAfter(segment, nextSegment);
segment = nextSegment;
next = next.getNext();
}
pointer.segment = first;
document.setDataLength(targetLength);
tryMergeArea(startFrom, insertedData.getDataSize());
} /*else if (pointer.segment instanceof MemorySegment) {
// TODO can't do that directly, also it breaks
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, insertedData, insertedDataOffset, insertedDataLength);
document.setDataLength(targetLength);
} */ else {
if (pointer.position < startFrom) {
splitSegment(startFrom);
focusSegment(startFrom);
}
MemorySegment insertedSegment = repository.createMemorySegment();
repository.insertMemoryData(insertedSegment, 0, insertedData, insertedDataOffset, insertedDataLength);
if (pointer.segment == null) {
segments.add(insertedSegment);
} else {
segments.addBefore(pointer.segment, insertedSegment);
}
pointer.segment = insertedSegment;
document.setDataLength(targetLength);
tryMergeArea(startFrom, insertedData.getDataSize());
}
document.notifyChangeListeners(this);
}
/**
* Directly inserts segment into given position.
*
* @param startFrom start position
* @param insertedSegment inserted segment
*/
public void insertSegment(long startFrom, DataSegment insertedSegment) {
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
long targetLength = document.getDataSize() + insertedSegment.getLength();
focusSegment(startFrom);
if (pointer.position < startFrom) {
splitSegment(startFrom);
focusSegment(startFrom);
}
if (pointer.segment == null) {
segments.add(insertedSegment);
} else {
segments.addBefore(pointer.segment, insertedSegment);
}
pointer.segment = insertedSegment;
document.setDataLength(targetLength);
document.notifyChangeListeners(this);
}
public void remove(long startFrom, long length) {
if (startFrom + length > document.getDataSize()) {
throw new OutOfBoundsException("Removed area is out of bounds");
}
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
if (length > 0) {
long targetLength = document.getDataSize() - length;
focusSegment(startFrom + length);
splitSegment(startFrom + length);
focusSegment(startFrom);
splitSegment(startFrom);
focusSegment(startFrom);
// Save position to return to
DataSegment prevSegment = (DataSegment) pointer.segment.getPrev();
long prevPointerPosition = prevSegment == null ? 0 : pointer.position - prevSegment.getLength();
// Drop all segments in given range
while (length > 0) {
length -= pointer.segment.getLength();
DataSegment next = segments.nextTo(pointer.segment);
repository.dropSegment(pointer.segment);
segments.remove(pointer.segment);
pointer.segment = next;
}
// Set pointer position
pointer.segment = prevSegment;
pointer.position = prevPointerPosition;
document.setDataLength(targetLength);
tryMergeSegments(startFrom);
}
document.notifyChangeListeners(this);
}
public void reset() {
pointer.setPointer(0, document.getSegments().first());
}
public void setDataSize(long dataSize) {
document.setDataSize(dataSize);
}
@Nonnull
public BinaryData copy() {
SegmentsRepository repository = document.getRepository();
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
DeltaDocument copy = repository.createDocument();
copy.setDataLength(getDataSize());
for (DataSegment segment : segments) {
copy.getSegments().add(repository.copySegment(segment));
}
return copy;
}
@Nonnull
public BinaryData copy(long startFrom, long length) {
SegmentsRepository repository = document.getRepository();
DeltaDocument copy = repository.createDocument();
copy.setDataLength(length);
focusSegment(startFrom);
if (length > 0) {
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
DataSegment segment = pointer.segment;
if (segment == null) {
throw new NullPointerException("Segment on given position not found");
}
long offset = startFrom - pointer.position;
while (length > 0) {
long segmentLength = segment.getLength();
long copyLength = segmentLength - offset;
if (copyLength > length) {
copyLength = length;
}
if (offset == 0 && copyLength == segmentLength) {
copy.getSegments().add(repository.copySegment(segment));
} else if (segment instanceof MemorySegment) {
MemorySegment memorySegment = (MemorySegment) segment;
copy.getSegments().add(repository.createMemorySegment(memorySegment.getSource(), memorySegment.getStartPosition() + offset, copyLength));
} else {
SourceSegment fileSegment = (SourceSegment) segment;
copy.getSegments().add(repository.createSourceSegment(fileSegment.getSource(), fileSegment.getStartPosition() + offset, copyLength));
}
length -= copyLength;
offset = 0;
segment = segments.nextTo(segment);
if (length > 0 && segment == null) {
throw new NullPointerException("Unexpected end of segments sequence");
}
}
}
return copy;
}
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
document.copyToArray(startFrom, target, offset, length);
}
/**
* Splits current pointer segment on given absolute position.
*
* @param position split position
*/
public void splitSegment(long position) {
long pointerPosition = pointer.position;
DataSegment pointerSegment = pointer.segment;
if (position < pointerPosition || (pointerSegment != null && position > pointerPosition + pointerSegment.getLength())) {
throw new IllegalStateException("Split position is out of current segment");
}
if (pointerPosition == position || pointerSegment == null || pointerPosition + pointerSegment.getLength() == position) {
// No action needed
return;
}
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
long firstPartSize = position - pointerPosition;
if (pointerSegment instanceof MemorySegment) {
MemorySegment memorySegment = (MemorySegment) pointerSegment;
MemorySegment newSegment = repository.createMemorySegment(memorySegment.getSource(), memorySegment.getStartPosition() + firstPartSize, memorySegment.getLength() - firstPartSize);
repository.updateSegmentLength(memorySegment, firstPartSize);
segments.addAfter(pointerSegment, newSegment);
} else {
SourceSegment fileSegment = (SourceSegment) pointerSegment;
SourceSegment newSegment = repository.createSourceSegment(fileSegment.getSource(), fileSegment.getStartPosition() + firstPartSize, fileSegment.getLength() - firstPartSize);
repository.updateSegmentLength(fileSegment, firstPartSize);
segments.addAfter(fileSegment, newSegment);
}
}
@Nullable
public DataSegment getSegment(long position) {
focusSegment(position);
return pointer.segment;
}
/**
* Returns segment starting from given position or copy of part of the
* segment starting from given position up to the end of length.
*
* @param position position
* @param length length
* @return data segment or null
*/
@Nullable
public DataSegment getPartCopy(long position, long length) {
focusSegment(position);
if (pointer.segment == null) {
return null;
}
SegmentsRepository repository = document.getRepository();
long offset = position - pointer.position;
long partLength = length;
if (pointer.segment.getLength() - offset < partLength) {
partLength = pointer.segment.getLength() - offset;
}
return repository.copySegment(pointer.segment, offset, partLength);
}
/**
* Focuses segment starting at or before given position and ending after it.
* <p>
* Returns null if position is at the end of the document and throws out of
* bounds exception otherwise.
*
* @param position requested position
* @throws OutOfBoundsException if position is before or after document
*/
private void focusSegment(long position) {
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
long dataSize = getDataSize();
if (position == 0) {
pointer.position = 0;
pointer.segment = segments.first();
return;
} else if (position == dataSize) {
pointer.position = dataSize;
pointer.segment = null;
return;
} else if (position < 0 || position > dataSize) {
throw new OutOfBoundsException("Position index out of range");
}
if (pointer.segment == null) {
pointer.segment = segments.last();
pointer.position = dataSize - pointer.segment.getLength();
}
if (position < pointer.position) {
if (pointer.segment == null && position == dataSize) {
pointer.segment = segments.last();
if (pointer.segment == null) {
throwNullSegmentException();
}
pointer.position -= pointer.segment.getLength();
}
while (position < pointer.position) {
pointer.segment = segments.prevTo(pointer.segment);
if (pointer.segment == null) {
throwNullSegmentException();
}
pointer.position -= pointer.segment.getLength();
}
} else {
if (pointer.segment == null) {
throwNullSegmentException();
}
while (position >= pointer.position + pointer.segment.getLength()) {
if ((pointer.segment.getNext() == null) && (position == pointer.position + pointer.segment.getLength())) {
break;
}
pointer.position += pointer.segment.getLength();
pointer.segment = segments.nextTo(pointer.segment);
if (pointer.segment == null) {
throwNullSegmentException();
}
}
}
}
private void tryMergeArea(long position, long length) {
tryMergeSegments(position);
// TODO
tryMergeSegments(position + length);
}
/**
* Attempts to merge segments at specified position.
*
* @param position target position
*/
private boolean tryMergeSegments(long position) {
if (position == 0 || position >= getDataSize()) {
return false;
}
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
SegmentsRepository repository = document.getRepository();
focusSegment(position);
DataSegment nextSegment = pointer.segment;
focusSegment(position - 1);
DataSegment segment = pointer.segment;
if (segment == nextSegment) {
return false;
}
if (segment instanceof SourceSegment && nextSegment instanceof SourceSegment) {
if (((SourceSegment) segment).getStartPosition() + segment.getLength() == ((SourceSegment) nextSegment).getStartPosition()) {
repository.updateSegmentLength(segment, segment.getLength() + nextSegment.getLength());
repository.dropSegment(nextSegment);
segments.remove(nextSegment);
return true;
}
return false;
}
if (segment instanceof MemorySegment && nextSegment instanceof MemorySegment) {
MemorySegment memorySegment = (MemorySegment) segment;
MemorySegment nextMemorySegment = (MemorySegment) nextSegment;
if (memorySegment.getSource() == nextMemorySegment.getSource()) {
if (memorySegment.getStartPosition() + segment.getLength() == nextMemorySegment.getStartPosition()) {
repository.updateSegmentLength(memorySegment, segment.getLength() + nextSegment.getLength());
repository.dropSegment(nextSegment);
segments.remove(nextSegment);
return true;
}
}
// TODO join two single memory segments?
}
return false;
}
public void validatePointerPosition() {
if (pointer.segment == null) {
if (pointer.position > 0 && pointer.position < getDataSize()) {
throw new IllegalStateException("Illegal pointer position " + pointer.position + " for null segment");
}
} else {
long segmentsLengthSum = 0;
DataSegment segment = pointer.segment.getPrev();
while (segment != null) {
segmentsLengthSum += segment.getLength();
segment = segment.getPrev();
}
if (segmentsLengthSum != pointer.position) {
throw new IllegalStateException("Illegal pointer position " + pointer.position);
}
}
}
private static void throwNullSegmentException() {
throw new IllegalStateException("Unexpected null segment");
}
/**
* POJO structure for sliding data pointer.
*/
private static class DataPointer {
long position;
@Nullable
DataSegment segment;
void setPointer(long position, @Nullable DataSegment segment) {
this.position = position;
this.segment = segment;
}
}
}
@@ -0,0 +1,182 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
/**
* Data source for binary data stored in memory.
*/
@ParametersAreNonnullByDefault
public class MemoryDataSource implements EditableBinaryData {
@Nonnull
private final EditableBinaryData data;
public MemoryDataSource(EditableBinaryData data) {
this.data = data;
}
@Override
public void setDataSize(long size) {
data.setDataSize(size);
}
@Override
public void setByte(long position, byte value) {
data.setByte(position, value);
}
@Override
public void insert(long startFrom, long length) {
data.insert(startFrom, length);
}
@Override
public void insert(long startFrom, byte[] insertedData) {
data.insert(startFrom, insertedData);
}
@Override
public void insert(long startFrom, BinaryData insertedData) {
data.insert(startFrom, insertedData);
}
@Override
public void remove(long startFrom, long length) {
data.remove(startFrom, length);
}
@Override
public boolean isEmpty() {
return data.isEmpty();
}
@Override
public long getDataSize() {
return data.getDataSize();
}
@Override
public byte getByte(long position) {
return data.getByte(position);
}
@Override
public void saveToStream(OutputStream outputStream) throws IOException {
data.saveToStream(outputStream);
}
@Nonnull
@Override
public BinaryData copy() {
return data.copy();
}
@Nonnull
@Override
public BinaryData copy(long startFrom, long length) {
return data.copy(startFrom, length);
}
@Override
public void insertUninitialized(long startFrom, long length) {
data.insertUninitialized(startFrom, length);
}
@Override
public void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength) {
data.insert(startFrom, insertedData, insertedDataOffset, insertedDataLength);
}
@Override
public void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
data.insert(startFrom, insertedData, insertedDataOffset, insertedDataLength);
}
@Override
public long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException {
return data.insert(startFrom, inputStream, maximumDataSize);
}
@Override
public void replace(long targetPosition, BinaryData replacingData) {
data.replace(targetPosition, replacingData);
}
@Override
public void replace(long targetPosition, BinaryData replacingData, long startFrom, long length) {
data.replace(targetPosition, replacingData, startFrom, length);
}
@Override
public void replace(long targetPosition, byte[] replacingData) {
data.replace(targetPosition, replacingData);
}
@Override
public void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length) {
data.replace(targetPosition, replacingData, replacingDataOffset, length);
}
@Override
public void fillData(long startFrom, long length) {
data.fillData(startFrom, length);
}
@Override
public void fillData(long startFrom, long length, byte fill) {
data.fillData(startFrom, length, fill);
}
@Override
public void clear() {
data.clear();
}
@Override
public void loadFromStream(InputStream inputStream) throws IOException {
data.loadFromStream(inputStream);
}
@Nonnull
@Override
public OutputStream getDataOutputStream() {
return data.getDataOutputStream();
}
@Override
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
data.copyToArray(startFrom, target, offset, length);
}
@Nonnull
@Override
public InputStream getDataInputStream() {
return data.getDataInputStream();
}
@Override
public void dispose() {
data.dispose();
}
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Data segment pointing to memory block.
*/
@ParametersAreNonnullByDefault
public class MemorySegment extends DataSegment {
@Nonnull
private MemoryDataSource source;
private long startPosition;
private long length;
public MemorySegment(MemoryDataSource source, long startPosition, long length) {
this.source = source;
this.startPosition = startPosition;
this.length = length;
}
@Nonnull
public MemoryDataSource getSource() {
return source;
}
public void setSource(MemoryDataSource source) {
this.source = source;
}
@Override
public long getStartPosition() {
return startPosition;
}
public void setStartPosition(long startPosition) {
this.startPosition = startPosition;
}
@Override
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public byte getByte(long position) {
return source.getByte(position);
}
@Nonnull
@Override
public DataSegment copy() {
return new MemorySegment(null, startPosition, length);
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
/**
* Memory segment creator.
*/
@ParametersAreNonnullByDefault
public interface MemorySegmentCreator {
/**
* Creates new memory segment.
*
* @return data page
*/
@Nonnull
EditableBinaryData createSegment();
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,75 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Data segment pointing to source data.
*/
@ParametersAreNonnullByDefault
public class SourceSegment extends DataSegment {
@Nonnull
private final DataSource source;
private long startPosition;
private long length;
public SourceSegment(DataSource source, long startPosition, long length) {
this.source = source;
this.startPosition = startPosition;
this.length = length;
}
@Nonnull
public DataSource getSource() {
return source;
}
@Override
public long getStartPosition() {
return startPosition;
}
public void setStartPosition(long startPosition) {
this.startPosition = startPosition;
}
@Override
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public byte getByte(long position) {
try {
return source.getByte(position);
} catch (IOException ex) {
throw new RuntimeException("Error while processing data source", ex);
}
}
@Nonnull
@Override
public DataSegment copy() {
return new SourceSegment(source, startPosition, length);
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Space placeholder segment.
*/
@ParametersAreNonnullByDefault
public class SpaceSegment extends DataSegment {
private long length;
public SpaceSegment(long length) {
this.length = length;
}
@Override
public long getStartPosition() {
return 0;
}
public void setStartPosition(long startPosition) {
throw new IllegalStateException("Unable to set start position on space placeholder segment");
}
@Override
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public byte getByte(long position) {
throw new IllegalStateException("Unable to read data from space placeholder segment");
}
@Nonnull
@Override
public DataSegment copy() {
return new SpaceSegment(length);
}
}
@@ -0,0 +1,113 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta.file;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Access window for delta data.
*/
@ParametersAreNonnullByDefault
public class DeltaDataPageWindow {
public static final int PAGE_SIZE = 1024;
@Nonnull
private final FileDataSource data;
@Nonnull
private final DataPage[] dataPages = new DataPage[]{new DataPage(), new DataPage()};
private int activeDataPage = 1;
public DeltaDataPageWindow(FileDataSource data) {
this.data = data;
dataPages[0].pageIndex = 0;
loadPage(0);
data.addCacheClearListener(() -> {
DeltaDataPageWindow.this.clearCache();
});
}
private void loadPage(int index) {
long pageIndex = dataPages[index].pageIndex;
long pagePosition = pageIndex * PAGE_SIZE;
try {
long fileLength = data.getDataLength();
byte[] page = dataPages[index].page;
int offset = 0;
int toRead = PAGE_SIZE;
if (pagePosition + PAGE_SIZE > fileLength) {
toRead = (int) (fileLength - pagePosition);
}
while (toRead > 0) {
int read = data.read(pagePosition, page, offset, toRead);
if (read == -1) {
throw new IOException("Unexpected read error ");
}
toRead -= read;
offset += read;
pagePosition += read;
}
} catch (IOException ex) {
Logger.getLogger(DeltaDataPageWindow.class.getName()).log(Level.SEVERE, null, ex);
}
}
public byte getByte(long position) {
long targetPageIndex = position / PAGE_SIZE;
int index = -1;
long pageIndex1 = dataPages[0].pageIndex;
long pageIndex2 = dataPages[1].pageIndex;
if (pageIndex1 == targetPageIndex) {
index = 0;
} else if (pageIndex2 == targetPageIndex) {
index = 1;
}
if (index == -1) {
DataPage dataPage = dataPages[activeDataPage];
dataPage.pageIndex = targetPageIndex;
loadPage(activeDataPage);
activeDataPage = (activeDataPage + 1) & 1;
return dataPage.page[(int) (position % PAGE_SIZE)];
}
return dataPages[index].page[(int) (position % PAGE_SIZE)];
}
/**
* Clears window cache.
*/
public void clearCache() {
dataPages[0].pageIndex = -1;
dataPages[1].pageIndex = -1;
}
/**
* Simple structure for data page.
*/
private static class DataPage {
long pageIndex = -1;
byte[] page;
public DataPage() {
page = new byte[PAGE_SIZE];
}
}
}
@@ -0,0 +1,155 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta.file;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.delta.DataSource;
/**
* Data source for access to file resource locking it for exclusive access.
*/
@ParametersAreNonnullByDefault
public class FileDataSource implements DataSource {
@Nonnull
private final File file;
@Nonnull
private final RandomAccessFile accessFile;
@Nonnull
private final DeltaDataPageWindow window;
private boolean closed = false;
private final List<CacheClearListener> listeners = new ArrayList<>();
public FileDataSource(File sourceFile, EditMode editMode) throws FileNotFoundException, IOException {
file = sourceFile;
accessFile = new RandomAccessFile(sourceFile, editMode.getFileAccessMode());
window = new DeltaDataPageWindow(this);
}
public FileDataSource(File sourceFile) throws FileNotFoundException, IOException {
this(sourceFile, EditMode.READ_WRITE);
}
@Override
public long getDataLength() throws IOException {
checkClosed();
return accessFile.length();
}
@Override
public void setDataLength(long length) throws IOException {
checkClosed();
accessFile.setLength(length);
}
@Nonnull
public File getFile() {
return file;
}
@Nonnull
/* package */ RandomAccessFile getAccessFile() {
checkClosed();
return accessFile;
}
@Override
public byte getByte(long position) throws IOException {
checkClosed();
return window.getByte(position);
}
@Override
public void setByte(long position, byte value) throws IOException {
checkClosed();
accessFile.seek(position);
accessFile.writeByte(value);
}
@Override
public int read(long position, byte[] buffer, int offset, int length) throws IOException {
accessFile.seek(position);
return accessFile.read(buffer, offset, length);
}
@Override
public void write(long position, byte[] buffer, int offset, int length) throws IOException {
accessFile.seek(position);
accessFile.write(buffer, offset, length);
}
/**
* Clears cache window.
*/
@Override
public void clearCache() {
for (CacheClearListener listener : listeners) {
listener.clearCache();
}
}
@Override
public void close() throws IOException {
checkClosed();
accessFile.close();
closed = true;
}
private void checkClosed() {
if (closed) {
throw new IllegalStateException("File was already closed");
}
}
public void addCacheClearListener(CacheClearListener listener) {
listeners.add(listener);
}
public void removeCacheClearListener(CacheClearListener listener) {
listeners.remove(listener);
}
public static interface CacheClearListener {
void clearCache();
}
@ParametersAreNonnullByDefault
public static enum EditMode {
READ_WRITE("rw"),
READ_ONLY("r");
@Nonnull
private final String fileAccessMode;
private EditMode(String fileAccessMode) {
this.fileAccessMode = fileAccessMode;
}
@Nonnull
public String getFileAccessMode() {
return fileAccessMode;
}
}
}
@@ -0,0 +1,446 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta.list;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
/**
* Default implementation of doubly linked list of items.
* @param <T> doubly linked list item
*/
@ParametersAreNonnullByDefault
public class DefaultDoublyLinkedList<T extends DoublyLinkedItem<T>> implements DoublyLinkedList<T> {
private T first;
/* Cached values. */
private T last;
private int size = 0;
@Nullable
@Override
public T first() {
return first;
}
@Nullable
@Override
public T last() {
return last;
}
@Nullable
@Override
public T nextTo(T item) {
return item.getNext();
}
@Nullable
@Override
public T prevTo(T item) {
return item.getPrev();
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Nullable
@Override
public T get(int index) {
T item = first;
while (index > 0) {
item = nextTo(item);
index--;
}
return item;
}
@Nonnull
@Override
public T set(int index, T item) {
T origItem = first;
while (index > 0) {
origItem = nextTo(origItem);
index--;
}
if (origItem == null) {
throw new OutOfBoundsException("No item for index " + index);
}
T itemPrev = origItem.getPrev();
T itemNext = origItem.getNext();
origItem.setNext(null);
origItem.setPrev(null);
item.setPrev(itemPrev);
item.setNext(itemNext);
if (last == origItem) {
last = item;
}
return origItem;
}
@Override
public boolean contains(Object item) {
T checkedItem = first;
while (checkedItem != null) {
if (checkedItem.equals(item)) {
return true;
}
checkedItem = nextTo(checkedItem);
}
return false;
}
@Override
@SuppressWarnings("unchecked")
public boolean containsAll(Collection<?> items) {
for (Object item : items) {
if (!contains((T) item)) {
return false;
}
}
return true;
}
@Nonnull
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private T current = first;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public T next() {
T result = current;
current = result.getNext();
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
};
}
@Nonnull
@Override
public Object[] toArray() {
if (last == null) {
return new Object[0];
}
int count = indexOf(last);
Object[] result = new Object[count];
int index = 0;
T item = first;
while (item != null) {
result[index] = item;
item = nextTo(item);
index++;
}
return result;
}
@Nonnull
@Override
@SuppressWarnings("unchecked")
public <X> X[] toArray(X[] template) {
int count = last == null ? 0 : indexOf(last);
X[] result = template.length >= count ? template : Arrays.copyOf(template, count);
int index = 0;
T item = first;
while (item != null) {
result[index] = (X) item;
item = nextTo(item);
index++;
}
return result;
}
@Override
public boolean add(T item) {
if (last != null) {
last.setNext(item);
item.setPrev(last);
item.setNext(null);
last = item;
} else {
first = item;
last = item;
item.setNext(null);
item.setPrev(null);
}
size++;
return true;
}
@Override
public void add(int index, T item) {
if (index == 0 && size == 0) {
add(item);
return;
}
if (index > size) {
throw new OutOfBoundsException("Index " + index + " is greater than " + size);
} else if (index == size) {
last.setNext(item);
item.setPrev(last);
item.setNext(null);
last = item;
} else if (index == 0) {
first.setPrev(item);
item.setNext(first);
first = item;
} else {
T indexItem = Objects.requireNonNull(get(index));
T prevItem = Objects.requireNonNull(indexItem.getPrev());
item.setPrev(prevItem);
item.setNext(indexItem);
prevItem.setNext(item);
indexItem.setPrev(item);
}
size++;
}
/**
* Adds item after given listItem.
* <p>
* ListItem must be part of the list otherwise list will become broken. Item
* should not be member of other list
*
* @param listItem item from the current list
* @param item newly added item
*/
public void addAfter(T listItem, T item) {
T listItemNext = listItem.getNext();
if (listItemNext == null) {
listItem.setNext(item);
item.setPrev(listItem);
item.setNext(null);
last = item;
} else {
listItem.setNext(item);
listItemNext.setPrev(item);
item.setPrev(listItem);
item.setNext(listItemNext);
}
size++;
}
public void addBefore(T targetItem, T item) {
T prev = targetItem.getPrev();
if (prev == null) {
targetItem.setPrev(item);
item.setNext(targetItem);
item.setPrev(null);
first = item;
} else {
targetItem.setPrev(item);
prev.setNext(item);
item.setNext(targetItem);
item.setPrev(prev);
}
size++;
}
@Override
public boolean addAll(Collection<? extends T> elementsToAdd) {
boolean result = false;
for (T element : elementsToAdd) {
result |= add(element);
}
return result;
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
for (T t : c) {
add(index, t);
index++;
}
return true;
}
@Override
public int indexOf(Object searchedItem) {
T item = first;
int index = 0;
while (item != null) {
if (item.equals(searchedItem)) {
return index;
}
item = item.getNext();
index++;
}
return -1;
}
@Override
@Nullable
public T remove(int index) {
T item = get(index);
if (item != null) {
removeItem(item);
return item;
}
return null;
}
@Override
public boolean remove(Object o) {
@SuppressWarnings("unchecked")
T item = (T) o;
if (item != null) {
removeItem(item);
return true;
}
return false;
}
private void removeItem(T item) {
if (item == first) {
T itemNext = item.getNext();
if (itemNext != null) {
itemNext.setPrev(null);
} else {
last = null;
}
first = itemNext;
} else {
T prev = Objects.requireNonNull(item.getPrev());
T next = item.getNext();
prev.setNext(next);
if (next != null) {
next.setPrev(prev);
} else {
last = prev;
}
}
size--;
item.setPrev(null);
item.setNext(null);
}
@Override
public boolean removeAll(Collection<?> collection) {
boolean result = false;
for (Object item : collection) {
@SuppressWarnings("unchecked")
T itemList = (T) item;
result |= remove(itemList);
}
return result;
}
@Override
public boolean retainAll(Collection<?> retainList) {
boolean changed = false;
if (first == null) {
return false;
}
T item = first;
do {
if (!retainList.contains(item)) {
changed = true;
T nextItem = item.getNext();
removeItem(item);
item = nextItem;
} else {
item = item.getNext();
}
} while (item != null);
return changed;
}
@Override
public void clear() {
first = null;
last = null;
size = 0;
}
@Override
public int lastIndexOf(Object searchedItem) {
if (size > 0) {
T item = last;
int index = size - 1;
while (item != null) {
if (item.equals(searchedItem)) {
return index;
}
item = item.getPrev();
index--;
}
}
return -1;
}
@Nonnull
@Override
public ListIterator<T> listIterator() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Nonnull
@Override
public ListIterator<T> listIterator(int index) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Nonnull
@Override
public List<T> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
@@ -0,0 +1,57 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta.list;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface for item of doubly linked list.
* @param <T> instance class
*/
@ParametersAreNonnullByDefault
public interface DoublyLinkedItem<T> {
/**
* Returns next linked item.
*
* @return next item
*/
@Nullable
T getNext();
/**
* Sets next linked item.
*
* @param next next item
*/
void setNext(@Nullable T next);
/**
* Returns previous linked item.
*
* @return next item
*/
@Nullable
T getPrev();
/**
* Sets previous linked item.
*
* @param previous previous item
*/
void setPrev(@Nullable T previous);
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.delta.list;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Doubly linked list of items.
* @param <T> doubly linked list item
*/
@ParametersAreNonnullByDefault
public interface DoublyLinkedList<T> extends List<T> {
/**
* Returns first item of the list.
*
* @return first item
*/
@Nullable
T first();
/**
* Returns last item of the list.
*
* @return last item
*/
@Nullable
T last();
/**
* Returns item next to given item.
*
* @param item item
* @return next item or null
*/
@Nullable
T nextTo(T item);
/**
* Returns item previous to given item.
*
* @param item item
* @return previous item or null
*/
@Nullable
T prevTo(T item);
}
@@ -0,0 +1,89 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.jna;
import com.sun.jna.Memory;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.buffer.BufferData;
/**
* Implementation of binary data interface using JNA byte buffer.
*/
@ParametersAreNonnullByDefault
public class JnaBufferData extends BufferData {
public JnaBufferData() {
this((ByteBuffer) null);
}
/**
* Creates instance directly wrapping provided byte buffer.
*
* @param data byte buffer
*/
public JnaBufferData(@Nullable ByteBuffer data) {
super(data != null ? data : JnaBufferData.allocateBufferInt(0));
}
/**
* Creates instance setting value to provided byte array.
*
* @param data byte array
*/
public JnaBufferData(@Nullable byte[] data) {
super(JnaBufferData.allocateBufferInt(data));
}
/**
* Creates instance with specified size.
*
* @param dataSize data size
*/
public JnaBufferData(int dataSize) {
super(JnaBufferData.allocateBufferInt(dataSize));
}
@Nonnull
@Override
protected ByteBuffer allocateBuffer(int capacity) {
return JnaBufferData.allocateBufferInt(capacity);
}
@Nonnull
private static ByteBuffer allocateBufferInt(int capacity) {
try {
return new Memory(capacity).getByteBuffer(0, capacity);
} catch (Throwable tw) {
// Fallback to regular byte buffer
return ByteBuffer.allocateDirect(capacity);
}
}
@Nonnull
private static ByteBuffer allocateBufferInt(@Nullable byte[] data) {
if (data == null) {
return JnaBufferData.allocateBufferInt(0);
} else {
ByteBuffer buffer = JnaBufferData.allocateBufferInt(data.length);
buffer.put(data);
buffer.clear();
return buffer;
}
}
}
@@ -0,0 +1,89 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.jna;
import com.sun.jna.Memory;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.buffer.BufferEditableData;
/**
* Implementation of editable binary data interface using JNA byte buffer.
*/
@ParametersAreNonnullByDefault
public class JnaBufferEditableData extends BufferEditableData {
public JnaBufferEditableData() {
this((ByteBuffer) null);
}
/**
* Creates instance directly wrapping provided byte buffer.
*
* @param data byte buffer
*/
public JnaBufferEditableData(@Nullable ByteBuffer data) {
super(data != null ? data : JnaBufferEditableData.allocateBufferInt(0));
}
/**
* Creates instance setting value to provided byte array.
*
* @param data byte array
*/
public JnaBufferEditableData(@Nullable byte[] data) {
super(JnaBufferEditableData.allocateBufferInt(data));
}
/**
* Creates instance with specified size.
*
* @param dataSize data size
*/
public JnaBufferEditableData(int dataSize) {
super(JnaBufferEditableData.allocateBufferInt(dataSize));
}
@Nonnull
@Override
protected ByteBuffer allocateBuffer(int capacity) {
return JnaBufferEditableData.allocateBufferInt(capacity);
}
@Nonnull
private static ByteBuffer allocateBufferInt(int capacity) {
try {
return new Memory(capacity).getByteBuffer(0, capacity);
} catch (Throwable tw) {
// Fallback to regular byte buffer
return ByteBuffer.allocateDirect(capacity);
}
}
@Nonnull
private static ByteBuffer allocateBufferInt(@Nullable byte[] data) {
if (data == null) {
return JnaBufferEditableData.allocateBufferInt(0);
} else {
ByteBuffer buffer = JnaBufferEditableData.allocateBufferInt(data.length);
buffer.put(data);
buffer.clear();
return buffer;
}
}
}
@@ -0,0 +1,75 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.jna.paged;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
import org.exbin.auxiliary.binary_data.jna.JnaBufferData;
import org.exbin.auxiliary.binary_data.buffer.paged.BufferPagedData;
import org.exbin.auxiliary.binary_data.paged.DataPageCreator;
/**
* Paged data stored using JNA byte buffer.
*/
@ParametersAreNonnullByDefault
public class JnaBufferPagedData extends BufferPagedData {
public JnaBufferPagedData() {
}
public JnaBufferPagedData(DataPageCreator dataPageCreator) {
this.dataPageCreator = dataPageCreator;
}
public JnaBufferPagedData(int pageSize) {
this.pageSize = pageSize;
}
@Nonnull
@Override
protected JnaBufferData createNewPage(byte[] pageData) {
if (dataPageCreator != null) {
EditableBinaryData page = dataPageCreator.createPage(pageData.length);
page.replace(0, pageData);
return (JnaBufferData) page;
}
return new JnaBufferData(pageData);
}
@Nonnull
@Override
protected JnaBufferData createNewPage(int pageDataSize) {
if (dataPageCreator != null) {
return (JnaBufferData) dataPageCreator.createPage(pageDataSize);
}
return new JnaBufferData(pageDataSize);
}
@Nullable
@Override
public DataPageCreator getDataPageCreator() {
return dataPageCreator;
}
@Override
public void setDataPageCreator(@Nullable DataPageCreator dataPageCreator) {
this.dataPageCreator = dataPageCreator;
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.paged;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Paged data with support for customizable page creator.
*/
public interface CustomizablePagedData extends PagedData {
/**
* Returns data page creator.
*
* @return data page creator
*/
@Nonnull
Optional<DataPageCreator> getDataPageCreator();
/**
* Sets data page creator.
*
* @param dataPageCreator data page creator
*/
void setDataPageCreator(@Nullable DataPageCreator dataPageCreator);
}
@@ -0,0 +1,34 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.paged;
import javax.annotation.Nonnull;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
/**
* Data page creator.
*/
public interface DataPageCreator {
/**
* Creates new data page with specified size.
*
* @param dataSize data size
* @return data page
*/
@Nonnull
EditableBinaryData createPage(int dataSize);
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.auxiliary.binary_data.paged;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
/**
* Interface for paged data.
* <p>
* Data are stored using block of data of the same size. Last page might be
* shorter than page size, but not empty.
*/
@ParametersAreNonnullByDefault
public interface PagedData extends EditableBinaryData {
/**
* Returns number of pages currently used.
*
* @return count of pages
*/
int getPagesCount();
/**
* Returns currently used page size.
*
* @return page size in bytes
*/
int getPageSize();
/**
* Gets data page allowing direct access to it.
*
* @param pageIndex page index
* @return data page
*/
@Nonnull
BinaryData getPage(int pageIndex);
/**
* Sets data page replacing existing page by reference.
*
* @param pageIndex page index
* @param dataPage data page
*/
void setPage(int pageIndex, BinaryData dataPage);
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Enumeration of cursor caret overlapping modes.
*
* @author ExBin Project (https://exbin.org)
*/
public enum CaretOverlapMode {
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,33 +32,34 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Input stream translation class which converts from input charset to target
* charset.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class CharsetStreamTranslator extends InputStream {
private static final String UNMAPPABLE_CHARACTER_ERROR = "Unmappable character should be handled automatically";
private static final String MALFORMED_CHARACTER_ERROR = "Malformed character should be handled automatically";
public static final String DEFAULT_ENCODING = "UTF-8";
public static final int DEFAULT_MAX_BYTES_PER_CHAR = 8;
public static final int BYTE_BUFFER_SIZE = 16;
@Nonnull
private final CharsetEncoder encoder;
protected final CharsetEncoder encoder;
@Nonnull
private final CharsetDecoder decoder;
protected final CharsetDecoder decoder;
@Nonnull
private final InputStream source;
protected final InputStream source;
@Nonnull
private final ByteBuffer inputBuffer;
protected final ByteBuffer inputBuffer;
@Nonnull
private final ByteBuffer outputBuffer;
protected final ByteBuffer outputBuffer;
@Nonnull
private final CharBuffer charBuffer;
private boolean endOfInput = false;
protected final CharBuffer charBuffer;
protected boolean endOfInput = false;
private int maxInputCharSize;
private int maxOutputCharSize;
protected int maxInputCharSize;
protected int maxOutputCharSize;
public CharsetStreamTranslator(Charset inputCharset, Charset outputCharset, InputStream source, int bufferSize) {
this.source = source;
@@ -110,7 +111,7 @@ public class CharsetStreamTranslator extends InputStream {
}
}
return outputBuffer.get();
return outputBuffer.get() & 0xFF;
}
@Override
@@ -162,9 +163,9 @@ public class CharsetStreamTranslator extends InputStream {
inputBuffer.position(inputBuffer.position() + 1);
}
} else if (decodeResult.isUnmappable()) {
throw new IllegalStateException("Unmappable character should be handled automatically");
throw new IllegalStateException(UNMAPPABLE_CHARACTER_ERROR);
} else if (decodeResult.isMalformed()) {
throw new IllegalStateException("Malformed character should be handled automatically");
throw new IllegalStateException(MALFORMED_CHARACTER_ERROR);
}
} while (charBuffer.position() == 0);
@@ -182,9 +183,9 @@ public class CharsetStreamTranslator extends InputStream {
} else if (encodeResult.isError()) {
throw new IllegalStateException("Unexpected error character");
} else if (encodeResult.isUnmappable()) {
throw new IllegalStateException("Unmappable character should be handled automatically");
throw new IllegalStateException(UNMAPPABLE_CHARACTER_ERROR);
} else if (encodeResult.isMalformed()) {
throw new IllegalStateException("Malformed character should be handled automatically");
throw new IllegalStateException(MALFORMED_CHARACTER_ERROR);
}
}
@@ -223,15 +224,15 @@ public class CharsetStreamTranslator extends InputStream {
int offset = position;
while (toRead > 0) {
try {
int red = source.read(buffer, offset, toRead);
if (red < 0) {
int read = source.read(buffer, offset, toRead);
if (read < 0) {
inputBuffer.limit(offset);
endOfInput = true;
break;
}
offset += red;
toRead -= red;
offset += read;
toRead -= read;
} catch (IOException ex) {
Logger.getLogger(CharsetStreamTranslator.class.getName()).log(Level.SEVERE, null, ex);
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,6 @@ package org.exbin.bined;
* <p>
* Used to specify what to do with basic clipboard actions like cut, copy, paste
* and delete.
*
* @author ExBin Project (https://exbin.org)
*/
public enum ClipboardHandlingMode {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,6 @@ import javax.annotation.Nullable;
/**
* Interface for code area caret.
*
* @author ExBin Project (https://exbin.org)
*/
public interface CodeAreaCaret {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface for code area caret change listener.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface CodeAreaCaretListener {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,6 @@ import javax.annotation.Nonnull;
/**
* Specifies caret position as combination of data position, section and code
* offset of code representation.
*
* @author ExBin Project (https://exbin.org)
*/
public interface CodeAreaCaretPosition {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Code Area control interface.
*
* @author ExBin Project (https://exbin.org)
*/
public interface CodeAreaControl extends DataProvider {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Code area section interface.
*
* @author ExBin Project (https://exbin.org)
*/
public interface CodeAreaSection {
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,13 +17,13 @@ package org.exbin.bined;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Selection between two positions where begin represents origin point and end
* of the selection can be before or after begin.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class CodeAreaSelection {
private long start;
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,12 +21,11 @@ import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
/**
* Binary editor component utilities.
*
* @author ExBin Project (https://exbin.org)
* Binary editor utilities.
*/
@ParametersAreNonnullByDefault
public class CodeAreaUtils {
@@ -36,6 +35,8 @@ public class CodeAreaUtils {
private static final int CODE_BUFFER_LENGTH = 16;
public static final String MIME_CLIPBOARD_BINARY = "application/octet-stream";
public static final String BINED_CLIPBOARD_MIME = "application/x-bined";
public static final String BINED_CLIPBOARD_MIME_FULL = BINED_CLIPBOARD_MIME + "; class=" + BinaryData.class.getCanonicalName();
public static final String CONTENT_DATA_ERROR = "Content data is null";
public static final String NULL_FIELD_ERROR = "Field cannot be null";
@@ -339,7 +340,7 @@ public class CodeAreaUtils {
* @param keyValue keyboard key value
* @param codeOffset current code offset
* @param codeType current code type
* @return true if key value value is valid
* @return true if key value is valid
*/
public static boolean isValidCodeKeyValue(char keyValue, int codeOffset, CodeType codeType) {
boolean validKey = false;
+1 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Code area zone identifier.
*
* @author ExBin Project (https://exbin.org)
*/
public interface CodeAreaZone {
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Enumeration of code characters case.
*
* @author ExBin Project (https://exbin.org)
*/
public enum CodeCharactersCase {
+1 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Enumeration of supported code types.
*
* @author ExBin Project (https://exbin.org)
*/
public enum CodeType {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Interface for data change listener.
*
* @author ExBin Project (https://exbin.org)
*/
public interface DataChangedListener {
+3 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,13 +15,11 @@
*/
package org.exbin.bined;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import org.exbin.auxiliary.binary_data.BinaryData;
/**
* Data provider.
*
* @author ExBin Project (https://exbin.org)
*/
public interface DataProvider {
@@ -30,7 +28,7 @@ public interface DataProvider {
*
* @return binary data
*/
@Nullable
@Nonnull
BinaryData getContentData();
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Specifies caret position as combination of data position, section and code
* offset of code representation.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class DefaultCodeAreaCaretPosition implements CodeAreaCaretPosition {
@@ -38,7 +36,7 @@ public class DefaultCodeAreaCaretPosition implements CodeAreaCaretPosition {
public DefaultCodeAreaCaretPosition() {
}
public DefaultCodeAreaCaretPosition(long dataPosition, int codeOffset, CodeAreaSection section) {
public DefaultCodeAreaCaretPosition(long dataPosition, int codeOffset, @Nullable CodeAreaSection section) {
this.dataPosition = dataPosition;
this.codeOffset = codeOffset;
this.section = section;
+1 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Enumeration of edit modes.
*
* @author ExBin Project (https://exbin.org)
*/
public enum EditMode {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface for edit mode change listener.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface EditModeChangedListener {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Enumeration of edit operations.
*
* @author ExBin Project (https://exbin.org)
*/
public enum EditOperation {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Enumeration of supported position code types.
*
* @author ExBin Project (https://exbin.org)
*/
public enum PositionCodeType {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Row wrapping mode.
*
* @author ExBin Project (https://exbin.org)
*/
public enum RowWrappingMode {
@@ -0,0 +1,23 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.bined;
/**
* Enumeration of scrollbar orientation variants.
*/
public enum ScrollBarOrientation {
HORIZONTAL, VERTICAL
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Enumeration of scrollbar visibility modes.
*
* @author ExBin Project (https://exbin.org)
*/
public enum ScrollBarVisibility {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined;
/**
* Interface for scrolling listener.
*
* @author ExBin Project (https://exbin.org)
*/
public interface ScrollingListener {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,14 +15,9 @@
*/
package org.exbin.bined;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface for selection change listener.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface SelectionChangedListener {
/**
+13 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,8 +22,6 @@ import javax.annotation.concurrent.Immutable;
/**
* Selection range is selection between two positions where begin represents
* origin point and end of the selection can be before or after begin.
*
* @author ExBin Project (https://exbin.org)
*/
@Immutable
public class SelectionRange {
@@ -49,10 +47,10 @@ public class SelectionRange {
/**
* Creates selection range from start to end including the start and not
* including the end position.
* including character at the end position.
*
* @param start selection start position
* @param end selection end position without actual end position itself
* @param end selection end position without character at the end position
*/
public SelectionRange(long start, long end) {
if (start < 0) {
@@ -67,10 +65,20 @@ public class SelectionRange {
this.end = end;
}
/**
* Returns selection start position.
*
* @return selection start position.
*/
public long getStart() {
return start;
}
/**
* Returns selection end position where selection is not including character at the end position.
*
* @return selection end position.
*/
public long getEnd() {
return end;
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
/**
* Border paint modes for basic painter.
*
* @author ExBin Project (https://exbin.org)
*/
public enum BasicBackgroundPaintMode {
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,6 @@ import org.exbin.bined.RowWrappingMode;
/**
* Code area data representation structure for basic variant.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class BasicCodeAreaLayout {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,35 +26,33 @@ import org.exbin.bined.capability.BasicScrollingCapable;
/**
* Code area scrolling.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class BasicCodeAreaScrolling {
@Nonnull
private final CodeAreaScrollPosition scrollPosition = new CodeAreaScrollPosition();
protected final CodeAreaScrollPosition scrollPosition = new CodeAreaScrollPosition();
@Nonnull
private ScrollBarVerticalScale scrollBarVerticalScale = ScrollBarVerticalScale.NORMAL;
private int horizontalExtentDifference;
private int verticalExtentDifference;
private int horizontalScrollBarHeight;
private int verticalScrollBarWidth;
protected ScrollBarVerticalScale scrollBarVerticalScale = ScrollBarVerticalScale.NORMAL;
protected int horizontalExtentDifference;
protected int verticalExtentDifference;
protected int horizontalScrollBarHeight;
protected int verticalScrollBarWidth;
private int lastVerticalScrollingValue = -1;
protected int lastVerticalScrollingValue = -1;
@Nonnull
private VerticalScrollUnit verticalScrollUnit = VerticalScrollUnit.ROW;
protected VerticalScrollUnit verticalScrollUnit = VerticalScrollUnit.ROW;
@Nonnull
private ScrollBarVisibility verticalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
protected ScrollBarVisibility verticalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
@Nonnull
private HorizontalScrollUnit horizontalScrollUnit = HorizontalScrollUnit.PIXEL;
protected HorizontalScrollUnit horizontalScrollUnit = HorizontalScrollUnit.PIXEL;
@Nonnull
private ScrollBarVisibility horizontalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
protected ScrollBarVisibility horizontalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
@Nonnull
private final CodeAreaScrollPosition maximumScrollPosition = new CodeAreaScrollPosition();
protected final CodeAreaScrollPosition maximumScrollPosition = new CodeAreaScrollPosition();
private static final long ROW_POSITION_LIMIT = Long.MAX_VALUE / Integer.MAX_VALUE;
protected int maximumScrollBarHeight = Integer.MAX_VALUE;
@Nullable
private Runnable verticalExtentChangeListener = null;
@@ -73,8 +71,7 @@ public class BasicCodeAreaScrolling {
this.verticalScrollBarWidth = verticalScrollBarWidth;
}
@Nonnull
public ScrollViewDimension computeViewDimension(int dataViewWidth, int dataViewHeight, BasicCodeAreaLayout layout, BasicCodeAreaStructure structure, int characterWidth, int rowHeight) {
public void computeViewDimension(ScrollViewDimension outputDimension, int dataViewWidth, int dataViewHeight, BasicCodeAreaLayout layout, BasicCodeAreaStructure structure, int characterWidth, int rowHeight) {
int charsPerRow = structure.getCharactersPerRow();
int dataWidth = layout.computePositionX(charsPerRow, characterWidth);
boolean fitsHorizontally = computeFitsHorizontally(dataViewWidth, dataWidth);
@@ -105,15 +102,14 @@ public class BasicCodeAreaScrolling {
height = recomputeScrollViewHeight(dataViewHeight, rowHeight, rowsPerData);
}
// TODO avoid creation of instance
return new ScrollViewDimension(dataViewWidth, dataViewHeight, width, height);
outputDimension.setDimension(dataViewWidth, dataViewHeight, width, height);
}
private boolean computeFitsHorizontally(int dataViewWidth, int dataWidth) {
protected boolean computeFitsHorizontally(int dataViewWidth, int dataWidth) {
return dataWidth <= dataViewWidth;
}
private boolean computeFitsVertically(int dataViewHeight, long rowsPerData, int rowHeight) {
protected boolean computeFitsVertically(int dataViewHeight, long rowsPerData, int rowHeight) {
int availableRows = (dataViewHeight + rowHeight - 1) / rowHeight;
if (rowsPerData > availableRows) {
return false;
@@ -122,7 +118,7 @@ public class BasicCodeAreaScrolling {
return rowsPerData * rowHeight <= dataViewHeight;
}
private int recomputeScrollViewWidth(int dataViewWidth, int characterWidth, int dataWidth, int charsPerRow) {
protected int recomputeScrollViewWidth(int dataViewWidth, int characterWidth, int dataWidth, int charsPerRow) {
int scrollViewWidth = 0;
switch (horizontalScrollUnit) {
case PIXEL: {
@@ -143,13 +139,13 @@ public class BasicCodeAreaScrolling {
return scrollViewWidth;
}
private int recomputeScrollViewHeight(int dataViewHeight, int rowHeight, long rowsPerData) {
protected int recomputeScrollViewHeight(int dataViewHeight, int rowHeight, long rowsPerData) {
int scrollViewHeight = 0;
switch (verticalScrollUnit) {
case PIXEL: {
if (rowsPerData > Integer.MAX_VALUE / rowHeight) {
if (rowsPerData > maximumScrollBarHeight / rowHeight) {
scrollBarVerticalScale = ScrollBarVerticalScale.SCALED;
scrollViewHeight = Integer.MAX_VALUE;
scrollViewHeight = maximumScrollBarHeight;
changeVerticalExtentDifference(0);
} else {
scrollBarVerticalScale = ScrollBarVerticalScale.NORMAL;
@@ -159,9 +155,9 @@ public class BasicCodeAreaScrolling {
break;
}
case ROW: {
if (rowsPerData > (Integer.MAX_VALUE - dataViewHeight)) {
if (rowsPerData > (maximumScrollBarHeight - dataViewHeight)) {
scrollBarVerticalScale = ScrollBarVerticalScale.SCALED;
scrollViewHeight = Integer.MAX_VALUE;
scrollViewHeight = maximumScrollBarHeight;
changeVerticalExtentDifference(0);
} else {
scrollBarVerticalScale = ScrollBarVerticalScale.NORMAL;
@@ -219,7 +215,7 @@ public class BasicCodeAreaScrolling {
long rest = rowsPerDocumentToLastPage % maxValue;
targetRow += (rest * scrollBarValue) / maxValue;
} else {
targetRow = (scrollBarValue * rowsPerDocumentToLastPage) / Integer.MAX_VALUE;
targetRow = (scrollBarValue * rowsPerDocumentToLastPage) / maximumScrollBarHeight;
}
scrollPosition.setRowPosition(targetRow);
}
@@ -245,7 +241,7 @@ public class BasicCodeAreaScrolling {
long rest = rowsPerDocumentToLastPage % maxValue;
targetRow += (rest * scrollBarValue) / maxValue;
} else {
targetRow = (scrollBarValue * rowsPerDocumentToLastPage) / Integer.MAX_VALUE;
targetRow = (scrollBarValue * rowsPerDocumentToLastPage) / maximumScrollBarHeight;
}
scrollPosition.setRowPosition(targetRow);
}
@@ -270,10 +266,10 @@ public class BasicCodeAreaScrolling {
case PIXEL: {
if (scrollBarVerticalScale == ScrollBarVerticalScale.SCALED) {
int scrollValue;
if (scrollPosition.getRowPosition() < ROW_POSITION_LIMIT) {
scrollValue = (int) ((scrollPosition.getRowPosition() * Integer.MAX_VALUE) / rowsPerDocument);
if (scrollPosition.getRowPosition() < Long.MAX_VALUE / maximumScrollBarHeight) {
scrollValue = (int) ((scrollPosition.getRowPosition() * maximumScrollBarHeight) / rowsPerDocument);
} else {
scrollValue = (int) (scrollPosition.getRowPosition() / (rowsPerDocument / Integer.MAX_VALUE));
scrollValue = (int) (scrollPosition.getRowPosition() / (rowsPerDocument / maximumScrollBarHeight));
}
return scrollValue;
}
@@ -282,10 +278,10 @@ public class BasicCodeAreaScrolling {
case ROW: {
if (scrollBarVerticalScale == ScrollBarVerticalScale.SCALED) {
int scrollValue;
if (scrollPosition.getRowPosition() < ROW_POSITION_LIMIT) {
scrollValue = (int) ((scrollPosition.getRowPosition() * Integer.MAX_VALUE) / rowsPerDocument);
if (scrollPosition.getRowPosition() < Long.MAX_VALUE / maximumScrollBarHeight) {
scrollValue = (int) ((scrollPosition.getRowPosition() * maximumScrollBarHeight) / rowsPerDocument);
} else {
scrollValue = (int) (scrollPosition.getRowPosition() / (rowsPerDocument / Integer.MAX_VALUE));
scrollValue = (int) (scrollPosition.getRowPosition() / (rowsPerDocument / maximumScrollBarHeight));
}
return scrollValue;
}
@@ -315,6 +311,14 @@ public class BasicCodeAreaScrolling {
this.horizontalExtentChangeListener = horizontalExtentChangeListener;
}
public int getMaximumScrollBarHeight() {
return maximumScrollBarHeight;
}
public void setMaximumScrollBarHeight(int maximumScrollBarHeight) {
this.maximumScrollBarHeight = maximumScrollBarHeight;
}
@Nonnull
public CodeAreaScrollPosition computeScrolling(CodeAreaScrollPosition startPosition, ScrollingDirection direction, int rowsPerPage, long rowsPerDocument) {
CodeAreaScrollPosition targetPosition = new CodeAreaScrollPosition();
@@ -469,7 +473,7 @@ public class BasicCodeAreaScrolling {
}
@Nonnull
private PositionScrollVisibility checkTopScrollVisibility(long rowPosition) {
protected PositionScrollVisibility checkTopScrollVisibility(long rowPosition) {
if (verticalScrollUnit == VerticalScrollUnit.ROW) {
return rowPosition < scrollPosition.getRowPosition() ? PositionScrollVisibility.NOT_VISIBLE : PositionScrollVisibility.VISIBLE;
}
@@ -485,7 +489,7 @@ public class BasicCodeAreaScrolling {
}
@Nonnull
private PositionScrollVisibility checkBottomScrollVisibility(long rowPosition, int rowsPerPage, int rowOffset, int rowHeight) {
protected PositionScrollVisibility checkBottomScrollVisibility(long rowPosition, int rowsPerPage, int rowOffset, int rowHeight) {
int sumOffset = scrollPosition.getRowOffset() + rowOffset;
long lastFullRow = scrollPosition.getRowPosition() + rowsPerPage;
@@ -507,7 +511,7 @@ public class BasicCodeAreaScrolling {
}
@Nonnull
private PositionScrollVisibility checkLeftScrollVisibility(int charsPosition) {
protected PositionScrollVisibility checkLeftScrollVisibility(int charsPosition) {
int charPos = scrollPosition.getCharPosition();
if (horizontalScrollUnit != HorizontalScrollUnit.PIXEL) {
return charsPosition < charPos ? PositionScrollVisibility.NOT_VISIBLE : PositionScrollVisibility.VISIBLE;
@@ -524,7 +528,7 @@ public class BasicCodeAreaScrolling {
}
@Nonnull
private PositionScrollVisibility checkRightScrollVisibility(int charsPosition, int charsPerPage, int charOffset, int characterWidth) {
protected PositionScrollVisibility checkRightScrollVisibility(int charsPosition, int charsPerPage, int charOffset, int characterWidth) {
int sumOffset = scrollPosition.getCharOffset() + charOffset;
int lastFullChar = scrollPosition.getCharPosition() + charsPerPage;
@@ -598,7 +602,6 @@ public class BasicCodeAreaScrolling {
return Optional.of(targetScrollPosition);
}
@Nonnull
public void updateMaximumScrollPosition(long rowsPerDocument, int rowsPerPage, int charactersPerRow, int charactersPerPage, int lastCharOffset, int lastRowOffset) {
maximumScrollPosition.reset();
if (rowsPerDocument > rowsPerPage) {
@@ -629,7 +632,7 @@ public class BasicCodeAreaScrolling {
}
}
private void setHorizontalScrollPosition(CodeAreaScrollPosition scrollPosition, int charPos, int pixelOffset, int characterWidth) {
protected void setHorizontalScrollPosition(CodeAreaScrollPosition scrollPosition, int charPos, int pixelOffset, int characterWidth) {
switch (horizontalScrollUnit) {
case CHARACTER: {
scrollPosition.setCharPosition(charPos);
@@ -720,7 +723,7 @@ public class BasicCodeAreaScrolling {
lastVerticalScrollingValue = -1;
}
private void changeVerticalExtentDifference(int newDifference) {
protected void changeVerticalExtentDifference(int newDifference) {
if (verticalExtentDifference != newDifference) {
verticalExtentDifference = newDifference;
if (verticalExtentChangeListener != null) {
@@ -729,7 +732,7 @@ public class BasicCodeAreaScrolling {
}
}
private void changeHorizontalExtentDifference(int newDifference) {
protected void changeHorizontalExtentDifference(int newDifference) {
if (horizontalExtentDifference != newDifference) {
horizontalExtentDifference = newDifference;
if (horizontalExtentChangeListener != null) {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,6 @@ import org.exbin.bined.CodeAreaSection;
/**
* Enumeration of basic cursor position section.
*
* @author ExBin Project (https://exbin.org)
*/
public enum BasicCodeAreaSection implements CodeAreaSection {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,28 +27,26 @@ import org.exbin.bined.capability.ViewModeCapable;
/**
* Code area data representation structure for basic variant.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class BasicCodeAreaStructure {
private final BasicCodeAreaLayout layout = new BasicCodeAreaLayout();
protected final BasicCodeAreaLayout layout = new BasicCodeAreaLayout();
@Nonnull
private CodeAreaViewMode viewMode = CodeAreaViewMode.DUAL;
protected CodeAreaViewMode viewMode = CodeAreaViewMode.DUAL;
@Nonnull
private CodeType codeType = CodeType.HEXADECIMAL;
protected CodeType codeType = CodeType.HEXADECIMAL;
private long dataSize;
protected long dataSize;
@Nonnull
private RowWrappingMode rowWrapping = RowWrappingMode.NO_WRAPPING;
private int maxBytesPerLine;
private int wrappingBytesGroupSize;
protected RowWrappingMode rowWrapping = RowWrappingMode.NO_WRAPPING;
protected int maxBytesPerLine;
protected int wrappingBytesGroupSize;
private long rowsPerDocument;
private int bytesPerRow;
private int charactersPerRow;
protected long rowsPerDocument;
protected int bytesPerRow;
protected int charactersPerRow;
public BasicCodeAreaStructure() {
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,6 @@ import org.exbin.bined.CodeAreaZone;
/**
* Enumeration of basic code area zones.
*
* @author ExBin Project (https://exbin.org)
*/
public enum BasicCodeAreaZone implements CodeAreaZone {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Code area scrolling position.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class CodeAreaScrollPosition {
@@ -30,22 +28,22 @@ public class CodeAreaScrollPosition {
/**
* Scrollbar document row position.
*/
private long rowPosition = 0;
protected long rowPosition = 0;
/**
* Scrollbar document row pixel offset position.
*/
private int rowOffset = 0;
protected int rowOffset = 0;
/**
* Scrollbar document character position.
*/
private int charPosition = 0;
protected int charPosition = 0;
/**
* Scrollbar document character pixel offset position.
*/
private int charOffset = 0;
protected int charOffset = 0;
public CodeAreaScrollPosition() {
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
/**
* Enumeration of supported view modes.
*
* @author ExBin Project (https://exbin.org)
*/
public enum CodeAreaViewMode {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Enumeration of modes for enter key handling.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public enum EnterKeyHandlingMode {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
/**
* Enumeration of horizontal scrolling unit sizes.
*
* @author ExBin Project (https://exbin.org)
*/
public enum HorizontalScrollUnit {
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
/**
* Enumeration of supported movement directions.
*
* @author ExBin Project (https://exbin.org)
*/
public enum MovementDirection {
UP, DOWN, LEFT, RIGHT,
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@ package org.exbin.bined.basic;
/**
* Enumeration of possible visibility states of caret position relative to
* scrolling area.
*
* @author ExBin Project (https://exbin.org)
*/
public enum PositionScrollVisibility {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
/**
* Enumeration of vertical scaling modes.
*
* @author ExBin Project (https://exbin.org)
*/
public enum ScrollBarVerticalScale {
/**
@@ -26,7 +24,7 @@ public enum ScrollBarVerticalScale {
*/
NORMAL,
/**
* Height is more than available range/precission of the scrollbar and must
* Height is more than available range/precision of the scrollbar and must
* be scaled.
*/
SCALED
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,24 +15,20 @@
*/
package org.exbin.bined.basic;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Scrolling view dimensions.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
@Immutable
public class ScrollViewDimension {
private final int dataViewWidth;
private final int dataViewHeight;
private final int width;
private final int height;
protected int dataViewWidth;
protected int dataViewHeight;
protected int width;
protected int height;
public ScrollViewDimension(int dataViewWidth, int dataViewHeight, int width, int height) {
public ScrollViewDimension() {
}
public void setDimension(int dataViewWidth, int dataViewHeight, int width, int height) {
this.dataViewWidth = dataViewWidth;
this.dataViewHeight = dataViewHeight;
this.width = width;
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
/**
* Enumeration of supported scrolling directions.
*
* @author ExBin Project (https://exbin.org)
*/
public enum ScrollingDirection {
UP, DOWN, LEFT, RIGHT,
@@ -0,0 +1,30 @@
/*
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exbin.bined.basic;
/**
* Enumeration of selection selecting modes.
*/
public enum SelectingMode {
/**
* Selection is not updated.
*/
NONE,
/**
* Selection is updated.
*/
SELECTING
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,12 +17,10 @@ package org.exbin.bined.basic;
/**
* Enumeration of modes for tab key handling.
*
* @author ExBin Project (https://exbin.org)
*/
public enum TabKeyHandlingMode {
/**
* Handle enter using java platform detection (default).
* Handle tab key using java platform detection (default).
*/
PLATFORM_SPECIFIC,
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
/**
* Enumeration of vertical scrolling unit sizes.
*
* @author ExBin Project (https://exbin.org)
*/
public enum VerticalScrollUnit {
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,6 @@ import org.exbin.bined.basic.BasicBackgroundPaintMode;
/**
* Support for background paint mode capability.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface BackgroundPaintCapable {
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,6 @@ import org.exbin.bined.basic.VerticalScrollUnit;
/**
* Support for scrolling control.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface BasicScrollingCapable {
@@ -60,7 +58,7 @@ public interface BasicScrollingCapable {
void setVerticalScrollUnit(VerticalScrollUnit verticalScrollUnit);
/**
* Returns horizotal scrollbar visibility mode.
* Returns horizontal scrollbar visibility mode.
*
* @return scrollbar visibility mode
*/
@@ -68,7 +66,7 @@ public interface BasicScrollingCapable {
ScrollBarVisibility getHorizontalScrollBarVisibility();
/**
* Sets horizotal scrollbar visibility mode.
* Sets horizontal scrollbar visibility mode.
*
* @param horizontalScrollBarVisibility scrollbar visibility mode
*/
@@ -1,5 +1,5 @@
/*
* Copyright (C) ExBin Project
* Copyright (C) ExBin Project, https://exbin.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,6 @@ import org.exbin.bined.CodeAreaCaretListener;
/**
* Support for caret / cursor capability.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface CaretCapable {
@@ -107,11 +105,11 @@ public interface CaretCapable {
*
* @param positionX x-coordinate
* @param positionY y-coordinate
* @param overflowMode overflow mode
* @param overlapMode caret overlapping mode
* @return mouse position
*/
@Nonnull
CodeAreaCaretPosition mousePositionToClosestCaretPosition(int positionX, int positionY, CaretOverlapMode overflowMode);
CodeAreaCaretPosition mousePositionToClosestCaretPosition(int positionX, int positionY, CaretOverlapMode overlapMode);
/**
* Returns if cursor should be visible in other sections.

Some files were not shown because too many files have changed in this diff Show More