Initial commit
This commit is contained in:
commit
37fe627477
278 changed files with 20987 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
/build/classes/
|
||||
/build/generated/
|
||||
/build/libs/
|
||||
/build/tmp/
|
||||
/build/reports
|
||||
/.gradle
|
||||
/build/test-results
|
||||
/.idea
|
||||
*.png
|
||||
deployToGitlab
|
37
.gitlab-ci.yml
Normal file
37
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,37 @@
|
|||
# This file is a template, and might need editing before it works on your project.
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
|
||||
|
||||
# This is a sample GitLab CI/CD configuration file that should run without any modifications.
|
||||
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
|
||||
# it uses echo commands to simulate the pipeline execution.
|
||||
#
|
||||
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
|
||||
# Stages run in sequential order, but jobs within stages run in parallel.
|
||||
#
|
||||
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
|
||||
#
|
||||
|
||||
default:
|
||||
image: openjdk:8
|
||||
|
||||
stages: # List of stages for jobs, and their order of execution
|
||||
- build
|
||||
- deploy
|
||||
|
||||
build-job: # This job runs in the build stage, which runs first.
|
||||
stage: build
|
||||
script:
|
||||
- echo "Compiling the code..."
|
||||
- ./gradlew jar
|
||||
- echo "Compile complete."
|
||||
|
||||
|
||||
deploy-job: # This job runs in the deploy stage.
|
||||
stage: deploy # It only runs when *both* jobs in the test stage complete successfully.
|
||||
script:
|
||||
- echo "Deploying application..."
|
||||
- ./gradlew publish
|
||||
- echo "Application successfully deployed."
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
6
.idea/discord.xml
generated
Normal file
6
.idea/discord.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
</component>
|
||||
</project>
|
17
.idea/misc.xml
generated
Normal file
17
.idea/misc.xml
generated
Normal file
|
@ -0,0 +1,17 @@
|
|||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="JavadocGenerationManager">
|
||||
<option name="OUTPUT_DIRECTORY" value="$PROJECT_DIR$/jdc" />
|
||||
<option name="OPTION_SCOPE" value="private" />
|
||||
<option name="OPTION_DOCUMENT_TAG_USE" value="true" />
|
||||
<option name="OPTION_DOCUMENT_TAG_AUTHOR" value="true" />
|
||||
<option name="OPTION_DOCUMENT_TAG_VERSION" value="true" />
|
||||
<option name="OTHER_OPTIONS" value="-encoding utf8 -docencoding utf8 -charset utf8" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
4
.idea/serialmonitor_settings.xml
generated
Normal file
4
.idea/serialmonitor_settings.xml
generated
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SerialMonitorSettings" BaudRate="9600" LineEndingsIndex="0" ShowStatusWidget="true" />
|
||||
</project>
|
124
.idea/uiDesigner.xml
generated
Normal file
124
.idea/uiDesigner.xml
generated
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
41
LICENSE
Normal file
41
LICENSE
Normal file
|
@ -0,0 +1,41 @@
|
|||
Lesser TudbuT License
|
||||
|
||||
Copyright (c) 2023 Daniel H.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, and/or distribute copies of the
|
||||
Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
1. The above copyright notice, this permission notice, and these conditions
|
||||
shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
2. The Software must not be (re-)sold or sublicensed without significant
|
||||
modifications.
|
||||
|
||||
3. If this Software is an application, that is a piece of Software which the
|
||||
end-user directly interacts with in order to do some task, or one which
|
||||
is run in a stand-alone fashion in order to provide a service, then the
|
||||
addition or improvement of usable and/or meaningful functionality is a
|
||||
significant modification. The user should also quickly be able to tell that
|
||||
they are using the modified Software, upon beginning to use it, on the
|
||||
basis of major improvements.
|
||||
|
||||
4. If this Software is a library, that is a collection of utilities that is
|
||||
NOT an application by itself, or is marketed as such, then the addition
|
||||
of any aspects that make it an application usable by an end-user are
|
||||
deemed significant modifications. An application may also be an extension
|
||||
of an already-existing application adding functionality which is usable
|
||||
and meaningful to the end-user. The developer should also quickly be able
|
||||
to tell that they are using the modified Software on the basis of major
|
||||
improvements.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
8
README.md
Normal file
8
README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/b57be0d44d5c4a10a22124f5815cc2bb)](https://www.codacy.com/gh/TudbuT/tuddylib/dashboard?utm_source=github.com&utm_medium=referral&utm_content=TudbuT/tuddylib&utm_campaign=Badge_Grade)
|
||||
|
||||
# tuddylib
|
||||
|
||||
|
||||
TuddyLIB's package hell has been unified.
|
||||
|
||||
To convert your own code, run `bash -c "find src -type f -print0 | while IFS= read -d \$'\0' s ; do (cat \"\$s\" | sed -E 's/([^.])tudbut\./\1de.tudbut./g' > \"\$s.tmp\") && mv \"\$s.tmp\" \"\$s\" && echo processed \"\$s\" ; done"`
|
84
build.gradle
Normal file
84
build.gradle
Normal file
|
@ -0,0 +1,84 @@
|
|||
import java.nio.file.Paths
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
compileJava {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.jetbrains:annotations:20.1.0'
|
||||
implementation 'junit:junit:4.13.1'
|
||||
implementation 'org.junit.jupiter:junit-jupiter:5.7.0'
|
||||
}
|
||||
|
||||
test {
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options.encoding = "UTF-8"
|
||||
source = sourceSets.main.allJava
|
||||
classpath = configurations.compile
|
||||
}
|
||||
|
||||
javadoc {
|
||||
doLast {
|
||||
def f; (f = new FileOutputStream("build/docs/javadoc/stylesheet.css")).write(
|
||||
new URL("https://raw.githubusercontent.com/TudbuT/tools/master/dark_javadoc.css").newInputStream().
|
||||
readLines().join("\n").getBytes()
|
||||
); f.close()
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
doLast {
|
||||
File jar = new File("build/libs/tuddylib.jar")
|
||||
File loc = new File("TuddyLIB.jar")
|
||||
jar.renameTo(loc)
|
||||
|
||||
ZipOutputStream out = new ZipOutputStream(new FileOutputStream("TuddyLIB-javadoc.zip"))
|
||||
new File("build/docs/javadoc").eachFileRecurse(groovy.io.FileType.FILES) {
|
||||
out.putNextEntry(new ZipEntry(Paths.get("build/docs/javadoc").relativize(it.toPath()).toString()))
|
||||
byte[] bytes = new byte[it.length() as int]
|
||||
new FileInputStream(it).read(bytes)
|
||||
out.write(bytes)
|
||||
out.closeEntry()
|
||||
}
|
||||
out.close()
|
||||
}
|
||||
}
|
||||
|
||||
//jar.dependsOn("javadoc")
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifact('TuddyLIB.jar')
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url = "${System.getenv('CI_API_V4_URL')}/groups/<group>/-/packages/maven"
|
||||
name = "GitLab"
|
||||
credentials(HttpHeaderCredentials) {
|
||||
name = 'Job-Token'
|
||||
value = System.getenv("CI_JOB_TOKEN")
|
||||
}
|
||||
authentication {
|
||||
header(HttpHeaderAuthentication)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
185
gradlew
vendored
Executable file
185
gradlew
vendored
Executable file
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
2
settings.gradle
Normal file
2
settings.gradle
Normal file
|
@ -0,0 +1,2 @@
|
|||
rootProject.name = 'tuddylib'
|
||||
|
17
src/main/java/de/tudbut/algorithm/Algorithm.java
Normal file
17
src/main/java/de/tudbut/algorithm/Algorithm.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
package de.tudbut.algorithm;
|
||||
|
||||
public abstract class Algorithm<T> {
|
||||
|
||||
public abstract T solve(Input<T>[] inputs);
|
||||
|
||||
|
||||
public static class Input<T> {
|
||||
protected T t;
|
||||
protected String[] data;
|
||||
|
||||
public Input(T t, String[] data) {
|
||||
this.t = t;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
124
src/main/java/de/tudbut/algorithm/AlgorithmAI.java
Normal file
124
src/main/java/de/tudbut/algorithm/AlgorithmAI.java
Normal file
|
@ -0,0 +1,124 @@
|
|||
package de.tudbut.algorithm;
|
||||
|
||||
import de.tudbut.parsing.TudSort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class AlgorithmAI<T> extends Algorithm<T> {
|
||||
|
||||
private ArrayList<Association> associations = new ArrayList<>();
|
||||
private int nextChoiceID = 1;
|
||||
|
||||
public AlgorithmAI(Association[] associations) {
|
||||
this.associations = new ArrayList<>(Arrays.asList(associations));
|
||||
for (int i = 0; i < associations.length; i++) {
|
||||
if(associations[i].choiceID > nextChoiceID + 1) {
|
||||
nextChoiceID = associations[i].choiceID + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AlgorithmAI() {
|
||||
}
|
||||
|
||||
public Association[] getAssociations() {
|
||||
return associations.toArray(new Association[0]);
|
||||
}
|
||||
|
||||
public void reward(float amount) {
|
||||
int lastChoiceID = nextChoiceID - 1;
|
||||
associations = new ArrayList<>(Arrays.asList(TudSort.sort(associations.toArray(new Association[0]), association -> association.choiceID)));
|
||||
for (int j = 1, i = associations.size() - 1; i >= 0; i--) {
|
||||
Association association = associations.get(i);
|
||||
if(association.choiceID < lastChoiceID) {
|
||||
j *= 0.5;
|
||||
lastChoiceID = association.choiceID;
|
||||
}
|
||||
association.score += amount * j;
|
||||
}
|
||||
}
|
||||
|
||||
public void punish(float amount) {
|
||||
int lastChoiceID = nextChoiceID - 1;
|
||||
associations = new ArrayList<>(Arrays.asList(TudSort.sort(associations.toArray(new Association[0]), association -> association.choiceID)));
|
||||
for (int j = 1, i = associations.size() - 1; i >= 0; i--) {
|
||||
Association association = associations.get(i);
|
||||
if(association.choiceID < lastChoiceID) {
|
||||
j *= 0.5;
|
||||
lastChoiceID = association.choiceID;
|
||||
}
|
||||
association.score -= amount * j;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T solve(Input<T>[] inputs) {
|
||||
float highest = -Float.MAX_VALUE;
|
||||
Input<T> best = null;
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
float score = score(inputs[i]);
|
||||
if(score > highest) {
|
||||
highest = score;
|
||||
best = inputs[i];
|
||||
}
|
||||
}
|
||||
if(best != null) {
|
||||
for (int i = 0; i < best.data.length; i++) {
|
||||
Association association = associationFromString(best.data[i]);
|
||||
if(association == null)
|
||||
continue;
|
||||
association.choiceID = nextChoiceID;
|
||||
}
|
||||
nextChoiceID++;
|
||||
return best.t;
|
||||
} else {
|
||||
nextChoiceID++;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private float score(Input<T> input) {
|
||||
float r = 0;
|
||||
for (int i = 0; i < input.data.length; i++) {
|
||||
try {
|
||||
int finalI = i;
|
||||
if (associations.stream().noneMatch(association -> association.string != null && input.data[finalI] != null && association.string.equals(input.data[finalI]))) {
|
||||
Association association = new Association();
|
||||
association.string = input.data[i];
|
||||
associations.add(association);
|
||||
}
|
||||
Association association = associationFromString(input.data[i]);
|
||||
if (association == null)
|
||||
continue;
|
||||
r += association.score;
|
||||
} catch (NullPointerException ignore) { }
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private Association associationFromString(String s) {
|
||||
for (int i = 0; i < associations.size(); i++) {
|
||||
if(associations.get(i).string.equals(s))
|
||||
return associations.get(i);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Association {
|
||||
private String string;
|
||||
private float score = 0.0f;
|
||||
private int choiceID = 0;
|
||||
|
||||
public Association() { }
|
||||
|
||||
public Association(String s, float score) {
|
||||
this.string = s;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return string + ":" + score + " " + choiceID + ";";
|
||||
}
|
||||
}
|
||||
}
|
53
src/main/java/de/tudbut/async/Async.java
Normal file
53
src/main/java/de/tudbut/async/Async.java
Normal file
|
@ -0,0 +1,53 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
* Include this with import static.
|
||||
*/
|
||||
public class Async {
|
||||
|
||||
static final ThreadLocal<TaskQueue> context = ThreadLocal.withInitial(() -> TaskQueue.main);
|
||||
|
||||
public static <T> T await(Task<T> task) {
|
||||
return task.await();
|
||||
}
|
||||
|
||||
public static void context(TaskQueue queue) {
|
||||
context.set(queue);
|
||||
}
|
||||
|
||||
public static <T> Task<T> t(TaskCallable<T> callable) {
|
||||
return new Task<>(callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be problematic if errors might happen in the callable. use t() instead.
|
||||
*/
|
||||
public static <T> Task<T> s(TaskCallable<T> callable) {
|
||||
return new Task<>(callable).ok();
|
||||
}
|
||||
|
||||
public static Task<Void> loop(TaskCallable<Boolean> condition, TaskCallable<Void> body) {
|
||||
return new Task<>((res, rej) -> {
|
||||
while(new Task<>(condition).err(rej).ok().await()) {
|
||||
new Task<>(body).err(rej).ok().await();
|
||||
((TaskQueue)Thread.currentThread()).processNextHere();
|
||||
}
|
||||
res.call(null);
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean unblockQueue() {
|
||||
if(Thread.currentThread() instanceof TaskQueue) {
|
||||
TaskQueue q = (TaskQueue)Thread.currentThread();
|
||||
if(q.queue.hasNext()) {
|
||||
while(q.queue.hasNext()) {
|
||||
q.processNextHere();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
11
src/main/java/de/tudbut/async/Callback.java
Normal file
11
src/main/java/de/tudbut/async/Callback.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
*/
|
||||
|
||||
public interface Callback<T> {
|
||||
|
||||
void call(T t);
|
||||
}
|
38
src/main/java/de/tudbut/async/CallbackList.java
Normal file
38
src/main/java/de/tudbut/async/CallbackList.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
*/
|
||||
|
||||
public class CallbackList<T> implements Callback<T> {
|
||||
|
||||
private final ArrayList<Callback<T>> callbacks = new ArrayList<>();
|
||||
private boolean done = false;
|
||||
|
||||
public void add(Callback<T> callback) {
|
||||
callbacks.add(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(T t) {
|
||||
done = true;
|
||||
for (int i = 0 ; i < callbacks.size() ; i++) {
|
||||
callbacks.get(i).call(t);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean done() {
|
||||
return done;
|
||||
}
|
||||
|
||||
public void setDone() {
|
||||
done = true;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return callbacks.size() != 0;
|
||||
}
|
||||
}
|
11
src/main/java/de/tudbut/async/ComposeCallback.java
Normal file
11
src/main/java/de/tudbut/async/ComposeCallback.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
*/
|
||||
|
||||
public interface ComposeCallback<T, R> {
|
||||
|
||||
void call(T t, Callback<R> resolve, Callback<Throwable> reject);
|
||||
}
|
11
src/main/java/de/tudbut/async/InternalReject.java
Normal file
11
src/main/java/de/tudbut/async/InternalReject.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
class InternalReject extends Error {
|
||||
Task<?> task;
|
||||
Reject real;
|
||||
|
||||
InternalReject(Task<?> task, Reject real) {
|
||||
this.task = task;
|
||||
this.real = real;
|
||||
}
|
||||
}
|
23
src/main/java/de/tudbut/async/Reject.java
Normal file
23
src/main/java/de/tudbut/async/Reject.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
*/
|
||||
|
||||
public class Reject extends Error {
|
||||
final Object real;
|
||||
|
||||
public Reject(Throwable throwable) {
|
||||
super(throwable);
|
||||
real = throwable;
|
||||
}
|
||||
public Reject(Object object) {
|
||||
super(object.toString());
|
||||
real = object;
|
||||
}
|
||||
|
||||
public <T> T getReal() {
|
||||
return (T) real;
|
||||
}
|
||||
}
|
23
src/main/java/de/tudbut/async/Resolve.java
Normal file
23
src/main/java/de/tudbut/async/Resolve.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
*/
|
||||
|
||||
public class Resolve extends RuntimeException {
|
||||
final Object real;
|
||||
|
||||
public Resolve(Throwable throwable) {
|
||||
super(throwable);
|
||||
real = throwable;
|
||||
}
|
||||
public Resolve(Object object) {
|
||||
super(object.toString());
|
||||
real = object;
|
||||
}
|
||||
|
||||
public <T> T getReal() {
|
||||
return (T) real;
|
||||
}
|
||||
}
|
115
src/main/java/de/tudbut/async/Task.java
Normal file
115
src/main/java/de/tudbut/async/Task.java
Normal file
|
@ -0,0 +1,115 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
*/
|
||||
|
||||
public class Task<T> {
|
||||
TaskQueue queue;
|
||||
final TaskCallable<T> callable;
|
||||
final CallbackList<T> resolve = new CallbackList<>();
|
||||
final CallbackList<Throwable> reject = new CallbackList<>();
|
||||
private boolean done = false;
|
||||
private T result = null;
|
||||
private Throwable rejection = null;
|
||||
boolean isAwaiting = false;
|
||||
Task<?> parent = null;
|
||||
|
||||
public Task(TaskCallable<T> callable) {
|
||||
this.callable = callable;
|
||||
resolve.add((t) -> this.result = t);
|
||||
}
|
||||
public Task(TaskCallable<T> callable, Class<T> clazz) {
|
||||
this.callable = callable;
|
||||
resolve.add((t) -> this.result = t);
|
||||
}
|
||||
|
||||
public Task<T> then(Callback<T> resolve) {
|
||||
this.resolve.add(resolve);
|
||||
if(this.resolve.done()) {
|
||||
resolve.call(result);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public <R> Task<R> compose(ComposeCallback<T, R> callback) {
|
||||
return new Task<R>((res, rej) -> {
|
||||
callback.call(result, res, rej);
|
||||
}).appendTo(this);
|
||||
}
|
||||
|
||||
private Task<T> appendTo(Task<?> task) {
|
||||
this.parent = task;
|
||||
task.then(done -> {
|
||||
task.queue.register(this);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public Task<T> err(Callback<Throwable> reject) {
|
||||
if(parent != null)
|
||||
parent.err(reject);
|
||||
this.reject.add(reject);
|
||||
return this;
|
||||
}
|
||||
|
||||
public T await() {
|
||||
try {
|
||||
if(!done) {
|
||||
isAwaiting = true;
|
||||
// If it is in the queue already
|
||||
if(Thread.currentThread() == queue) {
|
||||
while(!done) {
|
||||
// Work while awaiting
|
||||
queue.processNextHere();
|
||||
}
|
||||
}
|
||||
else {
|
||||
synchronized (this) {
|
||||
if(!done) {
|
||||
wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if(rejection != null && !reject.exists())
|
||||
throw new Reject(rejection);
|
||||
return result;
|
||||
}
|
||||
|
||||
void setDone(Throwable rejection) {
|
||||
if(this.rejection == null) {
|
||||
this.rejection = rejection;
|
||||
}
|
||||
if (!done) {
|
||||
synchronized (this) {
|
||||
done = true;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean done() {
|
||||
return done;
|
||||
}
|
||||
|
||||
public Task<T> ok() {
|
||||
return ok(Async.context.get());
|
||||
}
|
||||
public Task<T> ok(TaskQueue queue) {
|
||||
if(this.queue != null)
|
||||
return this;
|
||||
this.queue = queue;
|
||||
if (parent != null) {
|
||||
parent.ok(queue);
|
||||
return this;
|
||||
}
|
||||
queue.register(this);
|
||||
return this;
|
||||
}
|
||||
}
|
11
src/main/java/de/tudbut/async/TaskCallable.java
Normal file
11
src/main/java/de/tudbut/async/TaskCallable.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
*/
|
||||
|
||||
public interface TaskCallable<T> {
|
||||
|
||||
void execute(Callback<T> resolve, Callback<Throwable> reject) throws Resolve, Reject;
|
||||
}
|
193
src/main/java/de/tudbut/async/TaskQueue.java
Normal file
193
src/main/java/de/tudbut/async/TaskQueue.java
Normal file
|
@ -0,0 +1,193 @@
|
|||
package de.tudbut.async;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import de.tudbut.global.DebugStateManager;
|
||||
import de.tudbut.tools.Queue;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Jun 2022
|
||||
*/
|
||||
|
||||
public class TaskQueue extends Thread {
|
||||
|
||||
public static final TaskQueue main = new TaskQueue();
|
||||
|
||||
private boolean stop = false;
|
||||
final Queue<Task<?>> queue = new Queue<>();
|
||||
public final CallbackList<Throwable> rejectionHandlers = new CallbackList<>();
|
||||
private AtomicBoolean waiting = new AtomicBoolean();
|
||||
private boolean running = false;
|
||||
private boolean queueEmpty = true;
|
||||
|
||||
public TaskQueue() {
|
||||
this.start();
|
||||
while (!waiting.get());
|
||||
try {
|
||||
Thread.sleep(5);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
public Queue<Task<?>> stopProcessing() throws IllegalAccessException {
|
||||
if(this == main)
|
||||
throw new IllegalAccessException("Can't stop main queue!");
|
||||
if(stop)
|
||||
throw new IllegalStateException("Already stopped!");
|
||||
stop = true;
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
return queue;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
if(stop)
|
||||
throw new IllegalStateException("Already stopped!");
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
while (queue.hasNext() || running || !queueEmpty) {
|
||||
synchronized (queue) {
|
||||
try {
|
||||
queue.wait();
|
||||
}
|
||||
catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
stop = true;
|
||||
}
|
||||
|
||||
<T> Task<T> register(Task<T> task) {
|
||||
task.queue = this;
|
||||
queue.add(task);
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param task The task to make
|
||||
* @param clazz The clazz of the return value. This is only used to determine T.
|
||||
* @return The task that was created
|
||||
*/
|
||||
<T> Task<T> register(TaskCallable<T> task, Class<T> clazz) {
|
||||
return register(new Task<>(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if(DebugStateManager.isDebugEnabled())
|
||||
System.out.println("[TaskQueue] Thread started.");
|
||||
while (!stop) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
waiting.set(true);
|
||||
wait();
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
waiting.set(false);
|
||||
}
|
||||
if(stop)
|
||||
return;
|
||||
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
public void process() {
|
||||
while(queue.hasNext()) {
|
||||
queueEmpty = false;
|
||||
try {
|
||||
processNextHere();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
if(!rejectionHandlers.exists()) {
|
||||
System.err.println("!! Unhandled Task rejection:");
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
rejectionHandlers.call(e);
|
||||
}
|
||||
}
|
||||
queueEmpty = true;
|
||||
synchronized (queue) {
|
||||
queue.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void processNextHere() {
|
||||
try {
|
||||
if(!queue.hasNext())
|
||||
return;
|
||||
|
||||
running = true;
|
||||
Task<T> task = (Task<T>) queue.next();
|
||||
|
||||
// Execute the task. If it rejects using reject(), throw a reject to be handled by the exception block.
|
||||
// If it resolves using throwResolve, redirect that to task.resolve.
|
||||
// If it throws something, redirect that to task.reject if possible, otherwise throw a Reject of it to be handled.
|
||||
try {
|
||||
try {
|
||||
task.callable.execute((t) -> {
|
||||
if (!task.resolve.done())
|
||||
task.resolve.call(t);
|
||||
task.setDone(null);
|
||||
}, (t) -> {
|
||||
throw new InternalReject(task, new Reject(t));
|
||||
});
|
||||
}
|
||||
catch (Resolve resolve) {
|
||||
if (!task.resolve.done())
|
||||
task.resolve.call(resolve.getReal());
|
||||
task.setDone(null);
|
||||
}
|
||||
catch (Reject reject) {
|
||||
throw new InternalReject(task, reject);
|
||||
}
|
||||
catch (InternalReject r) { throw r; }
|
||||
catch (Throwable throwable) {
|
||||
throw new InternalReject(task, new Reject(throwable));
|
||||
}
|
||||
}
|
||||
catch(InternalReject rej) {
|
||||
reject(rej.task, rej.real.getReal());
|
||||
} finally {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
catch (Reject e) {
|
||||
if(!rejectionHandlers.exists()) {
|
||||
System.err.println("!! Unhandled Task rejection:");
|
||||
e.printStackTrace();
|
||||
e.getCause().printStackTrace();
|
||||
return;
|
||||
}
|
||||
rejectionHandlers.call(e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void reject(Task<T> task, Throwable real) {
|
||||
if (!task.reject.done()) {
|
||||
task.setDone(real);
|
||||
if (task.reject.exists() || task.isAwaiting) {
|
||||
try {
|
||||
task.reject.call(real);
|
||||
} catch(InternalReject r) {
|
||||
reject(r.task, r.real.getReal());
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Reject(real);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
26
src/main/java/de/tudbut/debug/Debug.java
Normal file
26
src/main/java/de/tudbut/debug/Debug.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package de.tudbut.debug;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Debug {
|
||||
|
||||
private static final Map<Thread, Map<Object, DebugProfiler>> debugProfilerMap = new HashMap<>();
|
||||
|
||||
public static DebugProfiler getDebugProfiler(java.lang.reflect.GenericDeclaration o, boolean allowFinished) {
|
||||
if(!debugProfilerMap.containsKey(Thread.currentThread()))
|
||||
debugProfilerMap.put(Thread.currentThread(), new HashMap<>());
|
||||
|
||||
if(debugProfilerMap.get(Thread.currentThread()).containsKey(o)) {
|
||||
DebugProfiler profiler = debugProfilerMap.get(Thread.currentThread()).get(o);
|
||||
if(allowFinished && profiler.isLocked()) {
|
||||
return profiler;
|
||||
}
|
||||
else if(!profiler.isLocked()) {
|
||||
return profiler;
|
||||
}
|
||||
}
|
||||
debugProfilerMap.get(Thread.currentThread()).put(o, new DebugProfiler(o.toString(), "init"));
|
||||
return debugProfilerMap.get(Thread.currentThread()).get(o);
|
||||
}
|
||||
}
|
194
src/main/java/de/tudbut/debug/DebugProfiler.java
Normal file
194
src/main/java/de/tudbut/debug/DebugProfiler.java
Normal file
|
@ -0,0 +1,194 @@
|
|||
package de.tudbut.debug;
|
||||
|
||||
import de.tudbut.parsing.TudSort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
|
||||
public class DebugProfiler {
|
||||
|
||||
private final String name;
|
||||
private final long start = currentTimeMillis();
|
||||
private long end;
|
||||
private final ArrayList<Section> sections = new ArrayList<>();
|
||||
private Section currentSection;
|
||||
private boolean locked = false;
|
||||
private Results results = null;
|
||||
private boolean dirty = true;
|
||||
|
||||
public static final class Section {
|
||||
public final String name;
|
||||
private long start;
|
||||
private long end = 0;
|
||||
private Results results;
|
||||
|
||||
private Section(String s) {
|
||||
name = s;
|
||||
start = new Date().getTime();
|
||||
}
|
||||
|
||||
private void end() {
|
||||
end = new Date().getTime();
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return end - start;
|
||||
}
|
||||
|
||||
public float getTimeRelative() {
|
||||
return (float) getTime() / (float) results.getTotalTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + ": " + getTime() + " (/" + results.getTotalTime() + ") (" + (getTimeRelative() * 100) + "%)";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Results {
|
||||
private long time;
|
||||
private Section[] sections;
|
||||
|
||||
public long getTotalTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public Section[] getSections() {
|
||||
return sections;
|
||||
}
|
||||
|
||||
public Section getSectionByName(String name) {
|
||||
for (int i = 0 ; i < sections.length ; i++) {
|
||||
if(sections[i].name.equals(name))
|
||||
return sections[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (int i = 0 ; i < sections.length ; i++) {
|
||||
s.append(sections[i].toString());
|
||||
if(i < sections.length - 1)
|
||||
s.append("\n");
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public DebugProfiler(String name, String startingSection) {
|
||||
this.name = name;
|
||||
currentSection = new Section(startingSection);
|
||||
}
|
||||
|
||||
public synchronized DebugProfiler next(String next) {
|
||||
checkLocked();
|
||||
dirty = true;
|
||||
currentSection.end();
|
||||
synchronized (sections) {
|
||||
sections.add(currentSection);
|
||||
}
|
||||
currentSection = new Section(next);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized DebugProfiler endAll() {
|
||||
checkLocked();
|
||||
dirty = true;
|
||||
currentSection.end();
|
||||
synchronized (sections) {
|
||||
sections.add(currentSection);
|
||||
}
|
||||
currentSection = null;
|
||||
end = currentTimeMillis();
|
||||
|
||||
locked = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized Results getResults() {
|
||||
if(!locked)
|
||||
throw new RuntimeException("DebugProfiler results requested before call to endAll()!");
|
||||
if(results == null) {
|
||||
return createResults();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public synchronized Results getTempResults() {
|
||||
checkLocked();
|
||||
end = currentTimeMillis();
|
||||
return createResults();
|
||||
}
|
||||
|
||||
private Results createResults() {
|
||||
results = new Results();
|
||||
results.time = end - start;
|
||||
optimize();
|
||||
results.sections = TudSort.sort(sections.toArray(new Section[0]), section -> -section.getTime());
|
||||
return results;
|
||||
}
|
||||
|
||||
public void optimize() {
|
||||
synchronized (sections) {
|
||||
if(dirty) {
|
||||
ArrayList<Section> realSections = new ArrayList<>();
|
||||
for (int i = 0 ; i < sections.size() ; i++) {
|
||||
Section sec = sections.get(i);
|
||||
sec.results = results;
|
||||
if (realSections.stream().noneMatch(section -> section.name.equals(sec.name))) {
|
||||
Section section = new Section(sec.name);
|
||||
section.start = 0;
|
||||
section.end = sec.getTime();
|
||||
section.results = results;
|
||||
realSections.add(sec);
|
||||
}
|
||||
else {
|
||||
for (int j = 0 ; j < realSections.size() ; j++) {
|
||||
if (realSections.get(j).name.equals(sections.get(i).name)) {
|
||||
realSections.get(j).end += sec.getTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sections.clear();
|
||||
sections.addAll(realSections);
|
||||
dirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void checkLocked() throws RuntimeException {
|
||||
if(locked)
|
||||
throw new RuntimeException("DebugProfiler modify requested after call to endAll()!");
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public synchronized void delete() {
|
||||
locked = true;
|
||||
sections.clear();
|
||||
currentSection = null;
|
||||
results = null;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void finalize() {
|
||||
delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + ":\n" + getTempResults().toString();
|
||||
}
|
||||
}
|
111
src/main/java/de/tudbut/global/DebugStateManager.java
Normal file
111
src/main/java/de/tudbut/global/DebugStateManager.java
Normal file
|
@ -0,0 +1,111 @@
|
|||
package de.tudbut.global;
|
||||
|
||||
import de.tudbut.logger.DetailedLogger;
|
||||
import de.tudbut.logger.EmptyLogger;
|
||||
import de.tudbut.logger.Logger;
|
||||
import de.tudbut.logger.LoggerSink;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class DebugStateManager {
|
||||
private static final ArrayList<AtomicReference<LoggerSink>> toBeUpdated = new ArrayList<>();
|
||||
private static final ArrayList<AtomicReference<LoggerSink>> dToBeUpdated = new ArrayList<>();
|
||||
private static final Logger debugLogger = new Logger("DEBUG");
|
||||
private static final DetailedLogger debugDLogger = new DetailedLogger("DEBUG");
|
||||
private static boolean debugEnabled = false;
|
||||
|
||||
static {
|
||||
updateState();
|
||||
}
|
||||
|
||||
public static void enableDebug() {
|
||||
debugEnabled = true;
|
||||
updateState();
|
||||
}
|
||||
|
||||
public static void disableDebug() {
|
||||
debugEnabled = false;
|
||||
updateState();
|
||||
}
|
||||
|
||||
public static boolean isDebugEnabled() {
|
||||
return debugEnabled;
|
||||
}
|
||||
|
||||
public static AtomicBoolean debugEnabled() {
|
||||
return new AtomicBoolean(debugEnabled);
|
||||
}
|
||||
|
||||
public static void updateState() {
|
||||
for (AtomicReference<LoggerSink> reference : toBeUpdated) {
|
||||
reference.set(getDebugLogger(reference.get().getName()));
|
||||
}
|
||||
for (AtomicReference<LoggerSink> reference : dToBeUpdated) {
|
||||
reference.set(getDebugDLogger(reference.get().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public static LoggerSink getDebugLogger() {
|
||||
if (isDebugEnabled())
|
||||
return debugLogger;
|
||||
else
|
||||
return new EmptyLogger("DEBUG");
|
||||
}
|
||||
|
||||
public static LoggerSink getDebugLogger(String name) {
|
||||
if (isDebugEnabled()) {
|
||||
if (name == null || name.equals("DEBUG")) {
|
||||
return debugLogger;
|
||||
}
|
||||
else
|
||||
return new Logger(name);
|
||||
}
|
||||
else
|
||||
return new EmptyLogger(name);
|
||||
}
|
||||
|
||||
public static AtomicReference<LoggerSink> debugLoggerReference() {
|
||||
AtomicReference<LoggerSink> reference = new AtomicReference<>(getDebugLogger());
|
||||
toBeUpdated.add(reference);
|
||||
return reference;
|
||||
}
|
||||
|
||||
public static AtomicReference<LoggerSink> debugLoggerReference(String name) {
|
||||
AtomicReference<LoggerSink> reference = new AtomicReference<>(getDebugLogger(name));
|
||||
toBeUpdated.add(reference);
|
||||
return reference;
|
||||
}
|
||||
|
||||
public static LoggerSink getDebugDLogger() {
|
||||
if (isDebugEnabled())
|
||||
return debugDLogger;
|
||||
else
|
||||
return new EmptyLogger("DEBUG");
|
||||
}
|
||||
|
||||
public static LoggerSink getDebugDLogger(String name) {
|
||||
if (isDebugEnabled()) {
|
||||
if (name == null || name.equals("DEBUG")) {
|
||||
return debugDLogger;
|
||||
}
|
||||
else
|
||||
return new DetailedLogger(name);
|
||||
}
|
||||
else
|
||||
return new EmptyLogger(name);
|
||||
}
|
||||
|
||||
public static AtomicReference<LoggerSink> debugDLoggerReference() {
|
||||
AtomicReference<LoggerSink> reference = new AtomicReference<>(getDebugDLogger());
|
||||
dToBeUpdated.add(reference);
|
||||
return reference;
|
||||
}
|
||||
|
||||
public static AtomicReference<LoggerSink> debugDLoggerReference(String name) {
|
||||
AtomicReference<LoggerSink> reference = new AtomicReference<>(getDebugDLogger(name));
|
||||
dToBeUpdated.add(reference);
|
||||
return reference;
|
||||
}
|
||||
}
|
116
src/main/java/de/tudbut/global/GlobalSyncQueue.java
Normal file
116
src/main/java/de/tudbut/global/GlobalSyncQueue.java
Normal file
|
@ -0,0 +1,116 @@
|
|||
package de.tudbut.global;
|
||||
|
||||
import de.tudbut.timer.Ticker;
|
||||
import de.tudbut.tools.ThrowingRunnable;
|
||||
import de.tudbut.tools.Queue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class GlobalSyncQueue {
|
||||
private static final Queue<ThrowingRunnable> queue = new Queue<>();
|
||||
private static final Queue<ThrowingRunnable> immediateQueue = new Queue<>();
|
||||
private static final ArrayList<Object[]> timers = new ArrayList<>();
|
||||
|
||||
static {
|
||||
initAndRunAsynchronously();
|
||||
}
|
||||
|
||||
public static void add(ThrowingRunnable runnable) {
|
||||
if(runnable == null)
|
||||
throw new IllegalArgumentException();
|
||||
queue.add(runnable);
|
||||
}
|
||||
|
||||
public static void addImmediate(ThrowingRunnable runnable) {
|
||||
if(runnable == null)
|
||||
throw new IllegalArgumentException();
|
||||
immediateQueue.add(runnable);
|
||||
}
|
||||
|
||||
public static void addTimer(Ticker ticker, AtomicBoolean stop, int delay) {
|
||||
addTimer(new Ticker() {
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
ticker.run();
|
||||
}
|
||||
|
||||
public boolean doRun() {
|
||||
return !stop.get();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void addTimer(Ticker ticker, int delay) {
|
||||
addTimer(new Ticker() {
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
ticker.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void addTimer(Ticker ticker) {
|
||||
timers.add(new Object[]{ticker, 0L});
|
||||
}
|
||||
|
||||
private static void initAndRun() {
|
||||
while (true) {
|
||||
if (immediateQueue.hasNext()) {
|
||||
try {
|
||||
immediateQueue.next().run();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (queue.hasNext()) {
|
||||
try {
|
||||
queue.next().run();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (immediateQueue.hasNext()) {
|
||||
try {
|
||||
immediateQueue.next().run();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// An enhanced loop will throw an exception, fuck you intellij for this shit suggestion
|
||||
for (int i = 0; i < timers.size(); i++) {
|
||||
Object[] timer = timers.get(i);
|
||||
if (new Date().getTime() - ((Long) timer[1]) >= ((Ticker) timer[0]).getDelay()) {
|
||||
if (((Ticker) timer[0]).doRun()) {
|
||||
((Ticker) timer[0]).run();
|
||||
timer[1] = new Date().getTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (immediateQueue.hasNext()) {
|
||||
try {
|
||||
immediateQueue.next().run();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void initAndRunAsynchronously() {
|
||||
new Thread(GlobalSyncQueue::initAndRun, "GlobalSyncQueue").start();
|
||||
}
|
||||
}
|
166
src/main/java/de/tudbut/gui/SettingsGUI.java
Normal file
166
src/main/java/de/tudbut/gui/SettingsGUI.java
Normal file
|
@ -0,0 +1,166 @@
|
|||
package de.tudbut.gui;
|
||||
|
||||
import de.tudbut.global.DebugStateManager;
|
||||
import de.tudbut.tools.FileRW;
|
||||
import de.tudbut.tools.Tools;
|
||||
import de.tudbut.window.Window;
|
||||
import de.tudbut.gui.settingsgui.Setting;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SettingsGUI {
|
||||
public final ArrayList<Setting> settings = new ArrayList<>();
|
||||
public final ArrayList<Runnable> changeListeners = new ArrayList<>();
|
||||
private final boolean save;
|
||||
private final Window window;
|
||||
private final String name;
|
||||
|
||||
public SettingsGUI(boolean saveToFile, String name) {
|
||||
this.save = saveToFile;
|
||||
window = new Window(name, name, false);
|
||||
this.name = name;
|
||||
|
||||
if (saveToFile)
|
||||
loadFromFile();
|
||||
}
|
||||
|
||||
public void open() {
|
||||
window.setSize(500, 500);
|
||||
window.open();
|
||||
|
||||
for (int i = 0; i < settings.size(); i++) {
|
||||
Setting setting = settings.get(i);
|
||||
for (int j = 0; j < i; j++) {
|
||||
Setting s = settings.get(j);
|
||||
if (s.id.equals(setting.id)) {
|
||||
setting = s;
|
||||
settings.remove(i);
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
JButton button = new JButton(
|
||||
setting.name + (
|
||||
setting.type != Setting.Type.BUTTON ? (
|
||||
": " + (
|
||||
setting.value ?
|
||||
setting.type == Setting.Type.ON_OFF ? "ON" : "YES" :
|
||||
setting.type == Setting.Type.ON_OFF ? "OFF" : "NO"
|
||||
)
|
||||
) : ""
|
||||
)
|
||||
);
|
||||
|
||||
Setting finalSetting = setting;
|
||||
button.addActionListener(actionEvent -> {
|
||||
finalSetting.value = !finalSetting.value;
|
||||
button.setText(
|
||||
finalSetting.name + (
|
||||
finalSetting.type != Setting.Type.BUTTON ? (
|
||||
": " + (
|
||||
finalSetting.value ?
|
||||
finalSetting.type == Setting.Type.ON_OFF ? "ON" : "YES" :
|
||||
finalSetting.type == Setting.Type.ON_OFF ? "OFF" : "NO"
|
||||
)
|
||||
) : ""
|
||||
)
|
||||
);
|
||||
for (Runnable changeListener : finalSetting.onChange) {
|
||||
changeListener.run();
|
||||
}
|
||||
for (Runnable changeListener : changeListeners) {
|
||||
changeListener.run();
|
||||
}
|
||||
if (save) {
|
||||
try {
|
||||
saveSettings();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
button.setLocation(0, i * 25);
|
||||
button.setSize(500, 25);
|
||||
window.frame.addComponentListener(new ComponentListener() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent componentEvent) {
|
||||
button.setSize(window.frame.getSize().width, 25);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentMoved(ComponentEvent componentEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown(ComponentEvent componentEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent componentEvent) {
|
||||
}
|
||||
});
|
||||
button.setVisible(true);
|
||||
window.frame.add(button);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveSettings() throws IOException {
|
||||
FileRW file = new FileRW("settings_" + name + ".map");
|
||||
file.setContent(Tools.mapToString(toMap()).replaceAll(";", ";\n"));
|
||||
}
|
||||
|
||||
public void loadFromFile() {
|
||||
if (Files.exists(Paths.get("settings_" + name + ".map"))) {
|
||||
try {
|
||||
fromMap(Tools.stringToMap(new FileRW("settings_" + name + ".map").getContent().join("")));
|
||||
}
|
||||
catch (Exception e) {
|
||||
DebugStateManager.getDebugDLogger("Setting parser").subChannel(name).error("Couldn't read settings! Resetting them.");
|
||||
try {
|
||||
new FileRW("settings_" + name + ".map").setContent(":ON_OFF@%I false;");
|
||||
}
|
||||
catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> toMap() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Setting setting : settings) {
|
||||
map.put(setting.id, setting.type + "@" + setting.name + ": " + setting.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public void fromMap(Map<String, String> map) {
|
||||
for (String key : map.keySet()) {
|
||||
try {
|
||||
Setting setting = new Setting();
|
||||
setting.id = key;
|
||||
String value = map.get(key);
|
||||
|
||||
setting.type = Setting.Type.valueOf(value.split("@")[0]);
|
||||
value = value.split("@")[1];
|
||||
setting.name = value.split(": ")[0];
|
||||
value = value.split(": ")[1];
|
||||
setting.value = Boolean.parseBoolean(value);
|
||||
|
||||
settings.add(setting);
|
||||
}
|
||||
catch (Exception e) {
|
||||
DebugStateManager.getDebugDLogger("Setting parser").subChannel(name).warn("Skipping bad setting: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
src/main/java/de/tudbut/gui/settingsgui/Setting.java
Normal file
17
src/main/java/de/tudbut/gui/settingsgui/Setting.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
package de.tudbut.gui.settingsgui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Setting {
|
||||
public final ArrayList<Runnable> onChange = new ArrayList<>();
|
||||
public Type type = Type.ON_OFF;
|
||||
public String name = "";
|
||||
public String id = "";
|
||||
public boolean value = false;
|
||||
|
||||
public enum Type {
|
||||
ON_OFF,
|
||||
YES_NO,
|
||||
BUTTON
|
||||
}
|
||||
}
|
50
src/main/java/de/tudbut/io/AdaptiveSocketInputStream.java
Normal file
50
src/main/java/de/tudbut/io/AdaptiveSocketInputStream.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
|
||||
import de.tudbut.tools.Queue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
public class AdaptiveSocketInputStream extends InputStream {
|
||||
|
||||
public final InputStream parent;
|
||||
public final Socket socket;
|
||||
private final Queue<Integer> cache = new Queue<>();
|
||||
|
||||
public AdaptiveSocketInputStream(Socket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
this.parent = socket.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if(cache.hasNext())
|
||||
return cache.next();
|
||||
else
|
||||
return parent.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
int timeout = socket.getSoTimeout();
|
||||
socket.setSoTimeout(1);
|
||||
try {
|
||||
while(true) cache.add(parent.read());
|
||||
} catch (IOException e) {
|
||||
Throwable throwable = e;
|
||||
// Expected!
|
||||
while (!(throwable instanceof SocketTimeoutException) && throwable != null) {
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
if (throwable == null)
|
||||
throw e;
|
||||
}
|
||||
socket.setSoTimeout(timeout);
|
||||
if(parent.available() != 0)
|
||||
return parent.available();
|
||||
return cache.size() + parent.available();
|
||||
}
|
||||
}
|
18
src/main/java/de/tudbut/io/Buffer.java
Normal file
18
src/main/java/de/tudbut/io/Buffer.java
Normal file
|
@ -0,0 +1,18 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
public class Buffer {
|
||||
private byte[] content;
|
||||
|
||||
protected Buffer() {
|
||||
}
|
||||
|
||||
public static Buffer create(int length) {
|
||||
Buffer buffer = new Buffer();
|
||||
buffer.content = new byte[length];
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public Object get() {
|
||||
return content;
|
||||
}
|
||||
}
|
61
src/main/java/de/tudbut/io/CLSPrintWriter.java
Normal file
61
src/main/java/de/tudbut/io/CLSPrintWriter.java
Normal file
|
@ -0,0 +1,61 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import de.tudbut.tools.ReflectUtil;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class CLSPrintWriter extends PrintWriter {
|
||||
|
||||
public String customLineSeparator = "\n";
|
||||
|
||||
public CLSPrintWriter(Writer writer) {
|
||||
super(writer);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(Writer writer, boolean b) {
|
||||
super(writer, b);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(OutputStream outputStream) {
|
||||
super(outputStream);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(OutputStream outputStream, boolean b) {
|
||||
super(outputStream, b);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(String s) throws FileNotFoundException {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(String s, String s1) throws FileNotFoundException, UnsupportedEncodingException {
|
||||
super(s, s1);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(File file) throws FileNotFoundException {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(File file, String s) throws FileNotFoundException, UnsupportedEncodingException {
|
||||
super(file, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println() {
|
||||
try {
|
||||
synchronized(this.lock) {
|
||||
if (this.out == null) {
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
this.out.write(customLineSeparator);
|
||||
if (ReflectUtil.getPrivateFieldByTypeIndex(PrintWriter.class, this, boolean.class, 0)) {
|
||||
this.out.flush();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedIOException var4) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IOException var5) {
|
||||
ReflectUtil.setPrivateFieldByTypeIndex(PrintWriter.class, this, boolean.class, 1, true);
|
||||
}
|
||||
}
|
||||
}
|
28
src/main/java/de/tudbut/io/CharBuffer.java
Normal file
28
src/main/java/de/tudbut/io/CharBuffer.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
public class CharBuffer extends Buffer {
|
||||
private char[] content;
|
||||
|
||||
private CharBuffer() {
|
||||
}
|
||||
|
||||
public static CharBuffer create(int length) {
|
||||
CharBuffer buffer = new CharBuffer();
|
||||
buffer.content = new char[length / Character.BYTES];
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static CharBuffer createN(int length) {
|
||||
CharBuffer buffer = new CharBuffer();
|
||||
buffer.content = new char[length];
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public Object get() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public String getAsString() {
|
||||
return new String(content);
|
||||
}
|
||||
}
|
15
src/main/java/de/tudbut/io/DirectBuffers.java
Normal file
15
src/main/java/de/tudbut/io/DirectBuffers.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class DirectBuffers {
|
||||
|
||||
public static ByteBuffer createDirectByteBuffer(int size) {
|
||||
return prepareBuffer(ByteBuffer.allocateDirect(size));
|
||||
}
|
||||
|
||||
private static ByteBuffer prepareBuffer(ByteBuffer buffer) {
|
||||
return buffer.order(ByteOrder.nativeOrder());
|
||||
}
|
||||
}
|
24
src/main/java/de/tudbut/io/DoubleBuffer.java
Normal file
24
src/main/java/de/tudbut/io/DoubleBuffer.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
public class DoubleBuffer extends Buffer {
|
||||
private double[] content;
|
||||
|
||||
private DoubleBuffer() {
|
||||
}
|
||||
|
||||
public static DoubleBuffer create(int length) {
|
||||
DoubleBuffer buffer = new DoubleBuffer();
|
||||
buffer.content = new double[length / Double.BYTES];
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static DoubleBuffer createN(int length) {
|
||||
DoubleBuffer buffer = new DoubleBuffer();
|
||||
buffer.content = new double[length];
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public Object get() {
|
||||
return content;
|
||||
}
|
||||
}
|
111
src/main/java/de/tudbut/io/FileBus.java
Normal file
111
src/main/java/de/tudbut/io/FileBus.java
Normal file
|
@ -0,0 +1,111 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import de.tudbut.tools.Lock;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.channels.OverlappingFileLockException;
|
||||
|
||||
/**
|
||||
* Use files as Sockets/SocketServers, unlimited clients/servers supported on every file
|
||||
*/
|
||||
public class FileBus extends File {
|
||||
{
|
||||
try {
|
||||
if(createNewFile());
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
final RandomAccessFile file = new RandomAccessFile(this, "rw");
|
||||
final InputStream i;
|
||||
final OutputStream o;
|
||||
|
||||
FileLock lock;
|
||||
final Lock localLock = new Lock();
|
||||
|
||||
{
|
||||
try {
|
||||
file.seek((int) file.length());
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
i = new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return file.read();
|
||||
}
|
||||
};
|
||||
o = new OutputStream() {
|
||||
@Override
|
||||
public void write(int i) throws IOException {
|
||||
file.write(i);
|
||||
}
|
||||
};
|
||||
}
|
||||
final TypedInputStream ir = new TypedInputStream(i);
|
||||
final TypedOutputStream ow = new TypedOutputStream(o);
|
||||
|
||||
public FileBus(String s) throws FileNotFoundException {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public FileBus(String s, String s1) throws FileNotFoundException {
|
||||
super(s, s1);
|
||||
}
|
||||
|
||||
public FileBus(File file, String s) throws FileNotFoundException {
|
||||
super(file, s);
|
||||
}
|
||||
|
||||
public FileBus(URI uri) throws FileNotFoundException {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
public FileBus(File file) throws FileNotFoundException {
|
||||
super(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
public TypedInputStream getTypedReader() {
|
||||
return ir;
|
||||
}
|
||||
|
||||
public TypedOutputStream getTypedWriter() {
|
||||
return ow;
|
||||
}
|
||||
|
||||
public OutputStream getWriter() {
|
||||
return o;
|
||||
}
|
||||
|
||||
public InputStream getReader() {
|
||||
return i;
|
||||
}
|
||||
|
||||
public void waitForInput() throws IOException {
|
||||
ir.waitForInput();
|
||||
}
|
||||
|
||||
public void startWrite() throws IOException {
|
||||
localLock.waitHere();
|
||||
localLock.lock();
|
||||
while (lock == null) {
|
||||
try {
|
||||
lock = file.getChannel().lock();
|
||||
} catch (OverlappingFileLockException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopWrite() throws IOException {
|
||||
if(lock != null) {
|
||||
lock.release();
|
||||
localLock.unlock();
|
||||
lock = null;
|
||||
}
|
||||
o.flush();
|
||||
}
|
||||
}
|
102
src/main/java/de/tudbut/io/FileBusTransmitter.java
Normal file
102
src/main/java/de/tudbut/io/FileBusTransmitter.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import de.tudbut.obj.NotSupportedException;
|
||||
import de.tudbut.timer.AsyncCatcher;
|
||||
import de.tudbut.type.Stoppable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Transmits a FileBus over a different InputStream. always use a separate instance of FileBus
|
||||
* to avoid losing data!
|
||||
*/
|
||||
public class FileBusTransmitter implements Stoppable {
|
||||
|
||||
@Override
|
||||
public void start() throws NotSupportedException {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public FileBusTransmitter(FileBus bus, InputStream i, OutputStream o, Executor e0, Executor e1, AsyncCatcher onError) {
|
||||
e0.execute(() -> runI(bus,i,onError));
|
||||
e1.execute(() -> runO(bus,o,onError));
|
||||
}
|
||||
|
||||
private void runI(FileBus bus, InputStream i, AsyncCatcher onError) {
|
||||
boolean locked = false;
|
||||
boolean wait = false;
|
||||
while (!isStopped()) {
|
||||
try {
|
||||
if (i.available() == 0) {
|
||||
if (!wait) {
|
||||
wait = true;
|
||||
Thread.sleep(1);
|
||||
continue;
|
||||
}
|
||||
else if(locked) {
|
||||
wait = false;
|
||||
locked = false;
|
||||
bus.stopWrite();
|
||||
}
|
||||
}
|
||||
int input = i.read();
|
||||
if(input != -1) {
|
||||
if(!locked) {
|
||||
locked = true;
|
||||
bus.startWrite();
|
||||
}
|
||||
bus.o.write(input);
|
||||
}
|
||||
else if(!wait) {
|
||||
wait = true;
|
||||
Thread.sleep(1);
|
||||
continue;
|
||||
}
|
||||
else if(locked) {
|
||||
wait = false;
|
||||
locked = false;
|
||||
bus.stopWrite();
|
||||
}
|
||||
wait = false;
|
||||
}
|
||||
catch (IOException | InterruptedException e) {
|
||||
try {
|
||||
onError.run(e);
|
||||
}
|
||||
catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runO(FileBus bus, OutputStream o, AsyncCatcher onError) {
|
||||
boolean locked = false;
|
||||
boolean wait = false;
|
||||
while (!isStopped()) {
|
||||
try {
|
||||
int input = bus.i.read();
|
||||
if(input != -1) {
|
||||
o.write(input);
|
||||
}
|
||||
else if(!wait) {
|
||||
wait = true;
|
||||
Thread.sleep(1);
|
||||
continue;
|
||||
}
|
||||
wait = false;
|
||||
}
|
||||
catch (IOException | InterruptedException e) {
|
||||
try {
|
||||
onError.run(e);
|
||||
}
|
||||
catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
src/main/java/de/tudbut/io/ImageIOUtils.java
Normal file
30
src/main/java/de/tudbut/io/ImageIOUtils.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import de.tudbut.tools.Tools;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ImageIOUtils {
|
||||
|
||||
public static int[] imageToBytes(BufferedImage image) {
|
||||
DataBuffer buffer = image.getData().getDataBuffer();
|
||||
|
||||
if(buffer.getClass() == DataBufferInt.class) {
|
||||
return ((DataBufferInt) buffer).getData();
|
||||
}
|
||||
if(buffer.getClass() == DataBufferByte.class) {
|
||||
return Tools.byteArrayToIntArray(((DataBufferByte) buffer).getData());
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public static BufferedImage imageFromBytes(int[] ints) throws IOException {
|
||||
return ImageIO.read(new ByteArrayInputStream(Tools.charArrayToByteArray(Tools.intArrayToCharArray(ints))));
|
||||
}
|
||||
}
|
24
src/main/java/de/tudbut/io/IntBuffer.java
Normal file
24
src/main/java/de/tudbut/io/IntBuffer.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
public class IntBuffer extends Buffer {
|
||||
private int[] content;
|
||||
|
||||
private IntBuffer() {
|
||||
}
|
||||
|
||||
public static IntBuffer create(int length) {
|
||||
IntBuffer buffer = new IntBuffer();
|
||||
buffer.content = new int[length / Integer.BYTES];
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static IntBuffer createN(int length) {
|
||||
IntBuffer buffer = new IntBuffer();
|
||||
buffer.content = new int[length];
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public Object get() {
|
||||
return content;
|
||||
}
|
||||
}
|
47
src/main/java/de/tudbut/io/LineReader.java
Normal file
47
src/main/java/de/tudbut/io/LineReader.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
public class LineReader {
|
||||
|
||||
public static String readLineLF(Reader stream) throws IOException {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
char[] chars = new char[1];
|
||||
stream.read(chars);
|
||||
while (chars[0] != 0x0A) {
|
||||
stream.read(chars);
|
||||
buffer.append(chars[0]);
|
||||
}
|
||||
buffer.setLength(buffer.length()-1);
|
||||
buffer.trimToSize();
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public static String readLineCRLForLF(Reader stream) throws IOException {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
char[] chars = new char[1];
|
||||
stream.read(chars);
|
||||
while (chars[0] != 0x0A) {
|
||||
stream.read(chars);
|
||||
buffer.append(chars[0]);
|
||||
}
|
||||
if(buffer.toString().endsWith("\r\n"))
|
||||
buffer.setLength(buffer.length()-1);
|
||||
buffer.trimToSize();
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public static String readLineCRLF(Reader stream) throws IOException {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
char[] chars = new char[1];
|
||||
stream.read(chars);
|
||||
while (chars[0] != 0x0A) {
|
||||
buffer.append(chars[0]);
|
||||
stream.read(chars);
|
||||
}
|
||||
buffer.setLength(buffer.length()-1);
|
||||
buffer.trimToSize();
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
60
src/main/java/de/tudbut/io/RawLineReader.java
Normal file
60
src/main/java/de/tudbut/io/RawLineReader.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author TudbuT
|
||||
* @since 03 Oct 2021
|
||||
*/
|
||||
|
||||
// Fuck BufferedReader, im making my own.
|
||||
public class RawLineReader extends InputStream {
|
||||
|
||||
private boolean isAtCR = false;
|
||||
private boolean readLineInvoked = false;
|
||||
|
||||
private final InputStream stream;
|
||||
|
||||
public RawLineReader(InputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int read() throws IOException {
|
||||
int i = stream.read();
|
||||
if(readLineInvoked && i == '\n' && isAtCR) {
|
||||
readLineInvoked = false;
|
||||
return read();
|
||||
}
|
||||
if(isAtCR) {
|
||||
isAtCR = false;
|
||||
}
|
||||
if(i == '\r') {
|
||||
isAtCR = true;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return stream.available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
public synchronized String readLine() throws IOException {
|
||||
String line = "";
|
||||
int i;
|
||||
boolean _isAtCR = isAtCR;
|
||||
while ((i = read()) != '\r' && (_isAtCR || i != '\n')) {
|
||||
if(i != '\n')
|
||||
line += (char) i;
|
||||
}
|
||||
readLineInvoked = true;
|
||||
return line;
|
||||
}
|
||||
}
|
156
src/main/java/de/tudbut/io/StreamReader.java
Normal file
156
src/main/java/de/tudbut/io/StreamReader.java
Normal file
|
@ -0,0 +1,156 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import de.tudbut.type.IntArrayList;
|
||||
import de.tudbut.obj.CarrierException;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
/**
|
||||
* Helper for reading {@link java.io.InputStream}
|
||||
*/
|
||||
public class StreamReader {
|
||||
|
||||
/**
|
||||
* Buffer size for reading. Directly affects speed.
|
||||
*/
|
||||
public static int BUFFER_SIZE = 4096;
|
||||
|
||||
/**
|
||||
* The stream to read from
|
||||
*/
|
||||
public final InputStream inputStream;
|
||||
|
||||
private boolean endReached = false;
|
||||
|
||||
/**
|
||||
* Constructs a StreamReader for an InputStream
|
||||
* @param stream The stream to read
|
||||
*/
|
||||
public StreamReader(InputStream stream) {
|
||||
this.inputStream = stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte from the input stream. Unaffected by {@link StreamReader#BUFFER_SIZE}
|
||||
* @return read byte
|
||||
* @throws IOException Inherited from {@link InputStream#read}
|
||||
* @throws ArrayIndexOutOfBoundsException When the stream end was reached
|
||||
*/
|
||||
public byte readNextByte() throws IOException, ArrayIndexOutOfBoundsException {
|
||||
int got = inputStream.read();
|
||||
if (got < 0) {
|
||||
endReached = true;
|
||||
throw new ArrayIndexOutOfBoundsException("Stream end reached");
|
||||
}
|
||||
return (byte) got;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte from the input stream. Unaffected by {@link StreamReader#BUFFER_SIZE}.
|
||||
* Byte is converted to int, being unsigned in the process.
|
||||
* @return read unsigned bytes
|
||||
* @throws IOException Inherited from {@link StreamReader#readNextByte()}
|
||||
* @throws ArrayIndexOutOfBoundsException Inherited from {@link StreamReader#readNextByte()}
|
||||
*/
|
||||
public int readNextUnsignedByte() throws IOException, ArrayIndexOutOfBoundsException {
|
||||
return Byte.toUnsignedInt(readNextByte());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads bytes from the input stream until end is reached. Unaffected by {@link StreamReader#BUFFER_SIZE}.
|
||||
* Byte is converted to int, being unsigned in the process.
|
||||
* @return read unsigned bytes
|
||||
* @throws IOException Inherited from {@link StreamReader#readNextUnsignedByte()}
|
||||
*/
|
||||
public int[] readAllAsUnsignedBytes() throws IOException {
|
||||
IntArrayList bytes = new IntArrayList();
|
||||
int currentByte;
|
||||
while (!endReached) {
|
||||
try {
|
||||
currentByte = readNextUnsignedByte();
|
||||
bytes.add(currentByte);
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException ignore) {
|
||||
}
|
||||
}
|
||||
return bytes.toIntArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads bytes from the input stream until end is reached. Affected by {@link StreamReader#BUFFER_SIZE}.
|
||||
* @return read bytes
|
||||
* @throws IOException Inherited from {@link StreamReader#readNextByte()}
|
||||
*/
|
||||
public byte[] readAllAsBytes() throws IOException {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
byte[] currentBytes = new byte[BUFFER_SIZE];
|
||||
int len;
|
||||
try {
|
||||
while ((len = inputStream.read(currentBytes)) > 0) {
|
||||
bytes.write(currentBytes, 0, len);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new CarrierException(e, bytes.toByteArray());
|
||||
}
|
||||
return bytes.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all bytes in the stream and converts them to a char[]
|
||||
* @return {@link Character} array (native)
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public char[] readAllAsChars() throws IOException {
|
||||
return new String(readAllAsBytes()).toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all bytes in the stream and converts them to a char[]
|
||||
* @param encoding The encoding to use for conversion
|
||||
* @return {@link Character} array (native)
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public char[] readAllAsChars(String encoding) throws IOException {
|
||||
return new String(readAllAsBytes(), encoding).toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link StreamReader#readAllAsChars()}, but returns a string instead.
|
||||
* @return The string
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public String readAllAsString() throws IOException {
|
||||
return new String(readAllAsBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all lines in the file. All line ending are supported (\r\n, \n, \r).
|
||||
* @return The lines
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public String[] readAllAsLines() throws IOException {
|
||||
return new String(readAllAsBytes()).replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link StreamReader#readAllAsChars()}, but returns a string instead.
|
||||
* @param encoding The encoding to use
|
||||
* @return The string
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public String readAllAsString(String encoding) throws IOException {
|
||||
return new String(readAllAsBytes(), encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all lines in the file. All line ending are supported (\r\n, \n, \r).
|
||||
* @param encoding The encoding to use
|
||||
* @return The lines
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public String[] readAllAsLines(String encoding) throws IOException {
|
||||
return new String(readAllAsBytes(), encoding).replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
||||
}
|
||||
|
||||
}
|
33
src/main/java/de/tudbut/io/StreamRedirect.java
Normal file
33
src/main/java/de/tudbut/io/StreamRedirect.java
Normal file
|
@ -0,0 +1,33 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import de.tudbut.timer.AsyncTask;
|
||||
import de.tudbut.type.Nothing;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
public class StreamRedirect {
|
||||
|
||||
public static AsyncTask<Nothing> redirect(InputStream from, OutputStream to, Map<Integer, Integer[]> replacers) {
|
||||
return new AsyncTask<>(() -> {
|
||||
int i;
|
||||
while ((i = from.read()) != -1) {
|
||||
block0:
|
||||
{
|
||||
for (Integer key : replacers.keySet()) {
|
||||
if (i == key) {
|
||||
Integer[] r = replacers.get(key);
|
||||
for (Integer j : r) {
|
||||
to.write(j);
|
||||
}
|
||||
break block0;
|
||||
}
|
||||
}
|
||||
to.write(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
68
src/main/java/de/tudbut/io/StreamWriter.java
Normal file
68
src/main/java/de/tudbut/io/StreamWriter.java
Normal file
|
@ -0,0 +1,68 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Write data to an {@link OutputStream}
|
||||
*/
|
||||
public class StreamWriter {
|
||||
/**
|
||||
* The output stream to write to
|
||||
*/
|
||||
public final OutputStream stream;
|
||||
|
||||
/**
|
||||
* Constructs a new StreamWriter
|
||||
* @param stream
|
||||
*/
|
||||
public StreamWriter(OutputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public void writeChars(char[] c) throws IOException {
|
||||
byte[] bytes = new String(c).getBytes();
|
||||
stream.write(bytes);
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
public void writeChars(char[] c, String encoding) throws IOException {
|
||||
byte[] bytes = new String(c).getBytes(encoding);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
writeByte(bytes[i]);
|
||||
}
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
public void writeByte(byte b) throws IOException {
|
||||
stream.write(Byte.toUnsignedInt(b));
|
||||
}
|
||||
|
||||
public void writeUnsignedByte(short b) throws IOException {
|
||||
stream.write(b);
|
||||
}
|
||||
|
||||
public void writeUnsignedByte(int b) throws IOException {
|
||||
stream.write(b);
|
||||
}
|
||||
|
||||
public void writeBytes(byte[] bytes) throws IOException {
|
||||
stream.write(bytes);
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
public void writeUnsignedBytes(int[] bytes) throws IOException {
|
||||
for (int theByte : bytes) {
|
||||
stream.write(theByte);
|
||||
}
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
public void writeUnsignedBytes(short[] bytes) throws IOException {
|
||||
for (short theByte : bytes) {
|
||||
stream.write(theByte);
|
||||
}
|
||||
stream.flush();
|
||||
}
|
||||
}
|
161
src/main/java/de/tudbut/io/TypedInputStream.java
Normal file
161
src/main/java/de/tudbut/io/TypedInputStream.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* InputStream for more data types
|
||||
*/
|
||||
public class TypedInputStream {
|
||||
|
||||
protected InputStream stream;
|
||||
public int last = -1;
|
||||
private final Object waitForInputLock = new Object();
|
||||
private final Object readLock = new Object();
|
||||
|
||||
public InputStream getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
private WriteDirection direction = WriteDirection.HIGH_FIRST;
|
||||
|
||||
public TypedInputStream(InputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public byte readByte() throws IOException {
|
||||
return (byte) read();
|
||||
}
|
||||
|
||||
public short readShort() throws IOException {
|
||||
short s = 0;
|
||||
if(direction == WriteDirection.HIGH_FIRST) {
|
||||
s += (read() << 8 * 1);
|
||||
s += (read() << 8 * 0);
|
||||
}
|
||||
else {
|
||||
s += (read() << 8 * 0);
|
||||
s += (read() << 8 * 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public char readChar() throws IOException {
|
||||
char c = 0;
|
||||
if(direction == WriteDirection.HIGH_FIRST) {
|
||||
c += (read() << 8 * 1);
|
||||
c += (read() << 8 * 0);
|
||||
}
|
||||
else {
|
||||
c += (read() << 8 * 0);
|
||||
c += (read() << 8 * 1);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
int i = 0;
|
||||
if(direction == WriteDirection.HIGH_FIRST) {
|
||||
i += (read() << 8 * 3);
|
||||
i += (read() << 8 * 2);
|
||||
i += (read() << 8 * 1);
|
||||
i += (read() << 8 * 0);
|
||||
}
|
||||
else {
|
||||
i += (read() << 8 * 0);
|
||||
i += (read() << 8 * 1);
|
||||
i += (read() << 8 * 2);
|
||||
i += (read() << 8 * 3);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
public long readLong() throws IOException {
|
||||
long l = 0;
|
||||
if(direction == WriteDirection.HIGH_FIRST) {
|
||||
l += ((long) read() << 8 * 7);
|
||||
l += ((long) read() << 8 * 6);
|
||||
l += ((long) read() << 8 * 5);
|
||||
l += ((long) read() << 8 * 4);
|
||||
l += ((long) read() << 8 * 3);
|
||||
l += ((long) read() << 8 * 2);
|
||||
l += ((long) read() << 8 * 1);
|
||||
l += ((long) read() << 8 * 0);
|
||||
}
|
||||
else {
|
||||
l += ((long) read() << 8 * 0);
|
||||
l += ((long) read() << 8 * 1);
|
||||
l += ((long) read() << 8 * 2);
|
||||
l += ((long) read() << 8 * 3);
|
||||
l += ((long) read() << 8 * 4);
|
||||
l += ((long) read() << 8 * 5);
|
||||
l += ((long) read() << 8 * 6);
|
||||
l += ((long) read() << 8 * 7);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
public double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
public boolean readBoolean() throws IOException {
|
||||
return (read() & 1) != 0;
|
||||
}
|
||||
|
||||
public boolean[] readBooleans() throws IOException {
|
||||
boolean[] booleans = new boolean[8];
|
||||
int i = read() & 0xff;
|
||||
for (int j = 0 ; j < 8 ; ) {
|
||||
booleans[j] = (i & 1 << 8 >> ++j ) != 0;
|
||||
}
|
||||
return booleans;
|
||||
}
|
||||
|
||||
public String readString() throws IOException {
|
||||
int i = readInt();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int j = 0 ; j < i ; j++) {
|
||||
builder.append(readChar());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void waitForInput() throws IOException {
|
||||
synchronized (waitForInputLock) {
|
||||
if(last != -1)
|
||||
return;
|
||||
while ((last = stream.read()) == -1) ;
|
||||
}
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
synchronized (readLock) {
|
||||
synchronized (waitForInputLock) {
|
||||
int i;
|
||||
if (last == -1) {
|
||||
while ((i = stream.read()) == -1) ;
|
||||
}
|
||||
else {
|
||||
i = last;
|
||||
last = -1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WriteDirection getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(WriteDirection direction) {
|
||||
if(direction == null)
|
||||
throw new IllegalArgumentException();
|
||||
this.direction = direction;
|
||||
}
|
||||
}
|
141
src/main/java/de/tudbut/io/TypedOutputStream.java
Normal file
141
src/main/java/de/tudbut/io/TypedOutputStream.java
Normal file
|
@ -0,0 +1,141 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* OutputStream for more data types
|
||||
*/
|
||||
public class TypedOutputStream {
|
||||
|
||||
public OutputStream getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
OutputStream stream;
|
||||
private WriteDirection direction = WriteDirection.HIGH_FIRST;
|
||||
|
||||
public TypedOutputStream(OutputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public byte writeByte(byte b) throws IOException {
|
||||
stream.write(Byte.toUnsignedInt(b));
|
||||
stream.flush();
|
||||
return b;
|
||||
}
|
||||
|
||||
public short writeShort(short s) throws IOException {
|
||||
if(direction == WriteDirection.HIGH_FIRST) {
|
||||
stream.write(s >> 8 * 1 & 0xff);
|
||||
stream.write(s >> 8 * 0 & 0xff);
|
||||
}
|
||||
else {
|
||||
stream.write(s >> 8 * 0 & 0xff);
|
||||
stream.write(s >> 8 * 1 & 0xff);
|
||||
}
|
||||
stream.flush();
|
||||
return s;
|
||||
}
|
||||
|
||||
public char writeChar(char c) throws IOException {
|
||||
if(direction == WriteDirection.HIGH_FIRST) {
|
||||
stream.write(c >> 8 * 1 & 0xff);
|
||||
stream.write(c >> 8 * 0 & 0xff);
|
||||
}
|
||||
else {
|
||||
stream.write(c >> 8 * 0 & 0xff);
|
||||
stream.write(c >> 8 * 1 & 0xff);
|
||||
}
|
||||
stream.flush();
|
||||
return c;
|
||||
}
|
||||
|
||||
public int writeInt(int i) throws IOException {
|
||||
if(direction == WriteDirection.HIGH_FIRST) {
|
||||
stream.write(i >> 8 * 3 & 0xff);
|
||||
stream.write(i >> 8 * 2 & 0xff);
|
||||
stream.write(i >> 8 * 1 & 0xff);
|
||||
stream.write(i >> 8 * 0 & 0xff);
|
||||
}
|
||||
else {
|
||||
stream.write(i >> 8 * 0 & 0xff);
|
||||
stream.write(i >> 8 * 1 & 0xff);
|
||||
stream.write(i >> 8 * 2 & 0xff);
|
||||
stream.write(i >> 8 * 3 & 0xff);
|
||||
}
|
||||
stream.flush();
|
||||
return i;
|
||||
}
|
||||
|
||||
public float writeFloat(float f) throws IOException {
|
||||
writeInt(Float.floatToIntBits(f));
|
||||
return f;
|
||||
}
|
||||
|
||||
public long writeLong(long l) throws IOException {
|
||||
if(direction == WriteDirection.HIGH_FIRST) {
|
||||
stream.write((int) (l >> 8 * 7 & 0xff));
|
||||
stream.write((int) (l >> 8 * 6 & 0xff));
|
||||
stream.write((int) (l >> 8 * 5 & 0xff));
|
||||
stream.write((int) (l >> 8 * 4 & 0xff));
|
||||
stream.write((int) (l >> 8 * 3 & 0xff));
|
||||
stream.write((int) (l >> 8 * 2 & 0xff));
|
||||
stream.write((int) (l >> 8 * 1 & 0xff));
|
||||
stream.write((int) (l >> 8 * 0 & 0xff));
|
||||
}
|
||||
else {
|
||||
stream.write((int) (l >> 8 * 0 & 0xff));
|
||||
stream.write((int) (l >> 8 * 1 & 0xff));
|
||||
stream.write((int) (l >> 8 * 2 & 0xff));
|
||||
stream.write((int) (l >> 8 * 3 & 0xff));
|
||||
stream.write((int) (l >> 8 * 4 & 0xff));
|
||||
stream.write((int) (l >> 8 * 5 & 0xff));
|
||||
stream.write((int) (l >> 8 * 6 & 0xff));
|
||||
stream.write((int) (l >> 8 * 7 & 0xff));
|
||||
}
|
||||
stream.flush();
|
||||
return l;
|
||||
}
|
||||
|
||||
public double writeDouble(double d) throws IOException {
|
||||
writeLong(Double.doubleToLongBits(d));
|
||||
return d;
|
||||
}
|
||||
|
||||
public boolean writeBoolean(boolean b) throws IOException {
|
||||
stream.write(b ? 1 : 0);
|
||||
stream.flush();
|
||||
return b;
|
||||
}
|
||||
|
||||
public boolean[] writeBooleans(boolean[] booleans) throws IOException {
|
||||
int i = 0;
|
||||
for (int j = 0 ; j < 8 ; ) {
|
||||
i += (booleans[j] ? 1 : 0) << 8 >> ++j;
|
||||
}
|
||||
stream.write(i);
|
||||
stream.flush();
|
||||
return booleans;
|
||||
}
|
||||
|
||||
public String writeString(String string) throws IOException {
|
||||
int i = string.length();
|
||||
writeInt(i);
|
||||
char[] chars = string.toCharArray();
|
||||
for (int j = 0 ; j < i ; j++) {
|
||||
writeChar(chars[j]);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
public WriteDirection getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(WriteDirection direction) {
|
||||
if(direction == null)
|
||||
throw new IllegalArgumentException();
|
||||
this.direction = direction;
|
||||
}
|
||||
}
|
29
src/main/java/de/tudbut/io/WriteDirection.java
Normal file
29
src/main/java/de/tudbut/io/WriteDirection.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
/**
|
||||
* Indicator for how to write/read in TypedOutputStream and TypedInputStream
|
||||
*/
|
||||
public enum WriteDirection {
|
||||
|
||||
/**
|
||||
* Integer 1 writes as
|
||||
* 0x00 0x00 0x00 0x01 <br>
|
||||
* <br>
|
||||
* Integer 256 writes as
|
||||
* 0x00 0x00 0x01 0x00 <br>
|
||||
* <br>
|
||||
* This is the standard for most java applications
|
||||
*/
|
||||
HIGH_FIRST,
|
||||
/**
|
||||
* Integer 1 writes as
|
||||
* 0x01 0x00 0x00 0x00 <br>
|
||||
* <br>
|
||||
* Integer 256 writes as
|
||||
* 0x00 0x01 0x00 0x00 <br>
|
||||
* <br>
|
||||
* This is most commonly used in C/C++
|
||||
*/
|
||||
LOW_FIRST,
|
||||
;
|
||||
}
|
53
src/main/java/de/tudbut/logger/DetailedLogger.java
Normal file
53
src/main/java/de/tudbut/logger/DetailedLogger.java
Normal file
|
@ -0,0 +1,53 @@
|
|||
package de.tudbut.logger;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.sql.Time;
|
||||
import java.util.Date;
|
||||
|
||||
public class DetailedLogger implements LoggerSink {
|
||||
String name;
|
||||
public PrintStream out;
|
||||
|
||||
{
|
||||
out = System.out;
|
||||
}
|
||||
|
||||
|
||||
public DetailedLogger(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public DetailedLogger subChannel(String subChannel) {
|
||||
PrintStream stream = out;
|
||||
return new DetailedLogger(name + "] [" + subChannel) {
|
||||
{
|
||||
out = stream;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void info(String string) {
|
||||
String time = String.valueOf(new Date().getTime());
|
||||
out.println("[ " + new Time(new Date().getTime()) + "." + time.substring(time.length() - 3) + " " + Thread.currentThread().getName() + " INFO ] [" + this.name + "] " + string);
|
||||
}
|
||||
|
||||
public void debug(String string) {
|
||||
String time = String.valueOf(new Date().getTime());
|
||||
out.println("[ " + new Time(new Date().getTime()) + "." + time.substring(time.length() - 3) + " " + Thread.currentThread().getName() + " DEBUG ] [" + this.name + "] " + string);
|
||||
}
|
||||
|
||||
public void warn(String string) {
|
||||
String time = String.valueOf(new Date().getTime());
|
||||
out.println("[ " + new Time(new Date().getTime()) + "." + time.substring(time.length() - 3) + " " + Thread.currentThread().getName() + " WARN ] [" + this.name + "] " + string);
|
||||
}
|
||||
|
||||
public void error(String string) {
|
||||
String time = String.valueOf(new Date().getTime());
|
||||
out.println("[ " + new Time(new Date().getTime()) + "." + time.substring(time.length() - 3) + " " + Thread.currentThread().getName() + " ERROR ] [" + this.name + "] " + string);
|
||||
}
|
||||
}
|
43
src/main/java/de/tudbut/logger/EmptyLogger.java
Normal file
43
src/main/java/de/tudbut/logger/EmptyLogger.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package de.tudbut.logger;
|
||||
|
||||
public class EmptyLogger implements LoggerSink {
|
||||
private final String name;
|
||||
|
||||
public EmptyLogger(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public EmptyLogger() {
|
||||
this.name = null;
|
||||
}
|
||||
|
||||
public LoggerSink subChannel(String subChannel) {
|
||||
return new EmptyLogger(name + "] [" + subChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String string) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String string) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String string) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String string) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
184
src/main/java/de/tudbut/logger/Logger.java
Normal file
184
src/main/java/de/tudbut/logger/Logger.java
Normal file
|
@ -0,0 +1,184 @@
|
|||
package de.tudbut.logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.sql.Time;
|
||||
import java.util.Date;
|
||||
|
||||
public class Logger implements LoggerSink {
|
||||
String name;
|
||||
public PrintStream out;
|
||||
|
||||
{
|
||||
out = System.out;
|
||||
}
|
||||
|
||||
public Logger(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Logger subChannel(String subChannel) {
|
||||
PrintStream stream = out;
|
||||
return new Logger(name + "] [" + subChannel) {
|
||||
{
|
||||
out = stream;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public PrintWriter infoAsWriter() {
|
||||
return new PrintWriter(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
info(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
public PrintStream infoAsStream() {
|
||||
return new PrintStream(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
info(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public PrintWriter debugAsWriter() {
|
||||
return new PrintWriter(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
debug(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
public PrintStream debugAsStream() {
|
||||
return new PrintStream(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
debug(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public PrintWriter warnAsWriter() {
|
||||
return new PrintWriter(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
warn(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
public PrintStream warnAsStream() {
|
||||
return new PrintStream(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
warn(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public PrintWriter errorAsWriter() {
|
||||
return new PrintWriter(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
error(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
public PrintStream errorAsStream() {
|
||||
return new PrintStream(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
error(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void info(String string) {
|
||||
out.println("[" + new Time(new Date().getTime()) + " INFO] [" + this.name + "] " + string);
|
||||
}
|
||||
|
||||
public void debug(String string) {
|
||||
out.println("[" + new Time(new Date().getTime()) + " DEBUG] [" + this.name + "] " + string);
|
||||
}
|
||||
|
||||
public void debug(Throwable throwable) {
|
||||
throwable.printStackTrace(debugAsWriter());
|
||||
}
|
||||
|
||||
public void warn(String string) {
|
||||
out.println("[" + new Time(new Date().getTime()) + " WARN] [" + this.name + "] " + string);
|
||||
}
|
||||
|
||||
public void warn(Exception exception) {
|
||||
exception.printStackTrace(warnAsWriter());
|
||||
}
|
||||
|
||||
public void error(String string) {
|
||||
out.println("[" + new Time(new Date().getTime()) + " ERROR] [" + this.name + "] " + string);
|
||||
}
|
||||
|
||||
public void error(Throwable throwable) {
|
||||
throwable.printStackTrace(errorAsWriter());
|
||||
}
|
||||
}
|
155
src/main/java/de/tudbut/logger/LoggerSink.java
Normal file
155
src/main/java/de/tudbut/logger/LoggerSink.java
Normal file
|
@ -0,0 +1,155 @@
|
|||
package de.tudbut.logger;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.PrintStream;
|
||||
|
||||
public interface LoggerSink {
|
||||
String getName();
|
||||
|
||||
LoggerSink subChannel(String subChannel);
|
||||
|
||||
default PrintWriter infoAsWriter() {
|
||||
return new PrintWriter(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
info(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
default PrintStream infoAsStream() {
|
||||
return new PrintStream(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
info(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
default PrintWriter debugAsWriter() {
|
||||
return new PrintWriter(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
debug(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
default PrintStream debugAsStream() {
|
||||
return new PrintStream(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
debug(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
default PrintWriter warnAsWriter() {
|
||||
return new PrintWriter(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
warn(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
default PrintStream warnAsStream() {
|
||||
return new PrintStream(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
warn(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
default PrintWriter errorAsWriter() {
|
||||
return new PrintWriter(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
error(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
default PrintStream errorAsStream() {
|
||||
return new PrintStream(new OutputStream() {
|
||||
String s = "";
|
||||
|
||||
@Override
|
||||
public void write(int i) {
|
||||
if((char) i == '\n') {
|
||||
error(s);
|
||||
s = "";
|
||||
return;
|
||||
}
|
||||
s += (char) i;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
void info(String string);
|
||||
|
||||
void debug(String string);
|
||||
|
||||
default void debug(Throwable throwable) {
|
||||
throwable.printStackTrace(debugAsWriter());
|
||||
}
|
||||
|
||||
void warn(String string);
|
||||
|
||||
default void warn(Exception exception) {
|
||||
exception.printStackTrace(warnAsWriter());
|
||||
}
|
||||
|
||||
void error(String string);
|
||||
|
||||
default void error(Throwable throwable) {
|
||||
throwable.printStackTrace(errorAsWriter());
|
||||
}
|
||||
}
|
10
src/main/java/de/tudbut/math/ComplexFunction.java
Normal file
10
src/main/java/de/tudbut/math/ComplexFunction.java
Normal file
|
@ -0,0 +1,10 @@
|
|||
package de.tudbut.math;
|
||||
|
||||
public interface ComplexFunction {
|
||||
|
||||
ComplexNumber solve(ComplexNumber n);
|
||||
|
||||
default ComplexNumber solve(double n) {
|
||||
return solve(ComplexNumber.create(n, 0));
|
||||
}
|
||||
}
|
55
src/main/java/de/tudbut/math/ComplexNumber.java
Normal file
55
src/main/java/de/tudbut/math/ComplexNumber.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
package de.tudbut.math;
|
||||
|
||||
import de.tudbut.type.Vector2d;
|
||||
|
||||
public class ComplexNumber {
|
||||
|
||||
private double real = 0;
|
||||
private double imaginary = 0;
|
||||
private Vector2d vec;
|
||||
public boolean autoRecalculate = true;
|
||||
|
||||
public double getReal() {
|
||||
return real;
|
||||
}
|
||||
|
||||
public double getImaginary() {
|
||||
return imaginary;
|
||||
}
|
||||
|
||||
public Vector2d getVec() {
|
||||
return vec;
|
||||
}
|
||||
|
||||
public void setReal(double real) {
|
||||
this.real = real;
|
||||
vec = null;
|
||||
if(autoRecalculate)
|
||||
recalculateVector();
|
||||
}
|
||||
|
||||
public void setImaginary(double imaginary) {
|
||||
this.imaginary = imaginary;
|
||||
vec = null;
|
||||
if(autoRecalculate)
|
||||
recalculateVector();
|
||||
}
|
||||
|
||||
private ComplexNumber() {}
|
||||
|
||||
public static ComplexNumber create(double real, double imaginary) {
|
||||
ComplexNumber n = new ComplexNumber();
|
||||
n.real = real;
|
||||
n.imaginary = imaginary;
|
||||
return n;
|
||||
}
|
||||
|
||||
public Vector2d recalculateVector() {
|
||||
vec = new Vector2d(real, imaginary);
|
||||
return vec;
|
||||
}
|
||||
|
||||
public Vector2d getVector() {
|
||||
return vec == null ? recalculateVector() : vec;
|
||||
}
|
||||
}
|
10
src/main/java/de/tudbut/math/Function.java
Normal file
10
src/main/java/de/tudbut/math/Function.java
Normal file
|
@ -0,0 +1,10 @@
|
|||
package de.tudbut.math;
|
||||
|
||||
public interface Function {
|
||||
|
||||
double solve(double n);
|
||||
|
||||
default double solve(ComplexNumber n) {
|
||||
return solve(n.getReal());
|
||||
}
|
||||
}
|
89
src/main/java/de/tudbut/net/http/HTTPContentType.java
Normal file
89
src/main/java/de/tudbut/net/http/HTTPContentType.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
/**
|
||||
* Content types
|
||||
*/
|
||||
public enum HTTPContentType {
|
||||
AAC( "audio/aac"),
|
||||
ABW( "application/x-abiword"),
|
||||
ARC( "application/x-freearc"),
|
||||
AVI( "video/x-msvideo"),
|
||||
AZW( "application/vnd.amazon.ebook"),
|
||||
BIN( "application/octet-stream"),
|
||||
BMP( "image/bmp"),
|
||||
BZ( "application/x-bzip"),
|
||||
BZ2( "application/x-bzip2"),
|
||||
CSH( "application/x-csh"),
|
||||
CSS( "text/css"),
|
||||
CSV( "text/csv"),
|
||||
DOC( "application/msword"),
|
||||
DOCX( "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
|
||||
EOT( "application/vnd.ms-fontobject"),
|
||||
EPUB( "application/epub+zip"),
|
||||
GZ( "application/gzip"),
|
||||
GIF( "image/gif"),
|
||||
HTML( "text/html"),
|
||||
HTTP( "message/http"),
|
||||
ICO( "image/vnd.microsoft.icon"),
|
||||
ICS( "text/calendar"),
|
||||
JAR( "application/java-archive"),
|
||||
JPG( "image/jpeg"),
|
||||
JS( "text/javascript"),
|
||||
JSON( "application/json"),
|
||||
JSONLD( "application/ld+json"),
|
||||
MID( "audio/midi"),
|
||||
MJS( "text/javascript"),
|
||||
MP3( "audio/mpeg"),
|
||||
MPEG( "video/mpeg"),
|
||||
MPKG( "application/vnd.apple.installer+xml"),
|
||||
ODP( "application/vnd.oasis.opendocument.presentation"),
|
||||
ODS( "application/vnd.oasis.opendocument.spreadsheet"),
|
||||
ODT( "application/vnd.oasis.opendocument.text"),
|
||||
OGG( "audio/ogg"),
|
||||
OGV( "video/ogg"),
|
||||
OGX( "application/ogg"),
|
||||
OPUS( "audio/opus"),
|
||||
OTF( "font/otf"),
|
||||
PNG( "image/png"),
|
||||
PDF( "application/pdf"),
|
||||
PHP( "application/x-httpd-php"),
|
||||
PPT( "application/vnd.ms-powerpoint"),
|
||||
PPTX( "application/vnd.openxmlformats-officedocument.presentationml.presentation"),
|
||||
RAR( "application/vnd.rar"),
|
||||
RTF( "application/rtf"),
|
||||
SH( "application/x-sh"),
|
||||
SVG( "image/svg+xml"),
|
||||
SWF( "application/x-shockwave-flash"),
|
||||
TAR( "application/x-tar"),
|
||||
TCN( "text/tcn"),
|
||||
TCNMAP( "application/tcnmap"),
|
||||
TS( "video/mp2t"),
|
||||
TTF( "font/ttf"),
|
||||
TXT( "text/plain"),
|
||||
VSD( "application/vnd.visio"),
|
||||
WAV( "audio/wav"),
|
||||
WEBA( "audio/webm"),
|
||||
WEBM( "video/webm"),
|
||||
WEBP( "image/webp"),
|
||||
WOFF( "font/woff"),
|
||||
WOFF2( "font/woff2"),
|
||||
XHTML( "application/xhtml+xml"),
|
||||
XLS( "application/vnd.ms-excel"),
|
||||
XLSX( "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
|
||||
XML( "application/xml"),
|
||||
XML_READABLE("text/xml"),
|
||||
XUL( "application/vnd.mozilla.xul+xml"),
|
||||
ZIP( "application/zip"),
|
||||
_3GP_AUDIO( "audio/3gpp"),
|
||||
_3GP_VIDEO( "video/3gpp"),
|
||||
_3G2_AUDIO( "audio/3gpp2"),
|
||||
_3G2_VIDEO( "video/3gpp2"),
|
||||
_7Z( "application/x-7z-compressed"),
|
||||
ANY( "*/*"),
|
||||
;
|
||||
public final String asHeaderString;
|
||||
|
||||
HTTPContentType(String asHeaderStringIn) {
|
||||
asHeaderString = asHeaderStringIn;
|
||||
}
|
||||
}
|
91
src/main/java/de/tudbut/net/http/HTTPHeader.java
Normal file
91
src/main/java/de/tudbut/net/http/HTTPHeader.java
Normal file
|
@ -0,0 +1,91 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
import de.tudbut.obj.TLMap;
|
||||
|
||||
/**
|
||||
* Header for HTTP exchanges
|
||||
*/
|
||||
public class HTTPHeader {
|
||||
private final String name;
|
||||
private final String value;
|
||||
private final TLMap<String, String> param;
|
||||
|
||||
public HTTPHeader(String s) {
|
||||
String[] spl = s.split(": ", 2);
|
||||
name = spl[0];
|
||||
spl = spl[1].split("; ");
|
||||
value = spl[0];
|
||||
param = new TLMap<>();
|
||||
for (int i = 1 ; i < spl.length ; i++) {
|
||||
try {
|
||||
String[] kv = spl[i].split("=", 2);
|
||||
if(kv[1].startsWith("\"")) {
|
||||
kv[1] = kv[1]
|
||||
.split("\"", 2)[1]
|
||||
.replaceAll("%", "%P")
|
||||
.replaceAll("\\\\", "%B")
|
||||
.replaceAll("%B%B", "\\")
|
||||
.replaceAll("%Bn", "\n")
|
||||
.replaceAll("%B\"", "\"")
|
||||
.replaceAll("%B", "\\")
|
||||
.replaceAll("%P", "%");
|
||||
kv[1] = kv[1].substring(0, kv.length - 2);
|
||||
}
|
||||
else {
|
||||
kv[1] = kv[1]
|
||||
.replaceAll("%", "%P")
|
||||
.replaceAll("\\\\", "%B")
|
||||
.replaceAll("%B%B", "\\")
|
||||
.replaceAll("%Bn", "\n")
|
||||
.replaceAll("%B", "\\")
|
||||
.replaceAll("%P", "%");
|
||||
}
|
||||
param.set(kv[0], kv[1]);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a HTTPHeader from a key and a value
|
||||
* @param nameIn Key
|
||||
* @param valueIn Value
|
||||
*/
|
||||
public HTTPHeader(String nameIn, String valueIn) {
|
||||
this(nameIn, valueIn, new TLMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a HTTPHeader from a key, a value, and a parameter
|
||||
* @param nameIn Key
|
||||
* @param valueIn Value
|
||||
* @param paramIn Parameter
|
||||
*/
|
||||
public HTTPHeader(String nameIn, String valueIn, TLMap<String, String> paramIn) {
|
||||
name = nameIn;
|
||||
value = valueIn;
|
||||
param = paramIn;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return The form used in the request
|
||||
*/
|
||||
public String toString() {
|
||||
String m = TLMap.mapToString(param).replaceAll(":", "=\"").replaceAll(";", "\"; ");
|
||||
if(param.size() > 0)
|
||||
m = m.substring(0, m.length()-2);
|
||||
return name + ": " + value + (param.size() == 0 ? "" : "; " + m);
|
||||
}
|
||||
|
||||
public String key() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public TLMap<String, String> parameter() {
|
||||
return param.clone();
|
||||
}
|
||||
}
|
224
src/main/java/de/tudbut/net/http/HTTPRequest.java
Normal file
224
src/main/java/de/tudbut/net/http/HTTPRequest.java
Normal file
|
@ -0,0 +1,224 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
import de.tudbut.global.DebugStateManager;
|
||||
import de.tudbut.io.StreamReader;
|
||||
import de.tudbut.io.StreamWriter;
|
||||
import de.tudbut.obj.DoubleTypedObject;
|
||||
import de.tudbut.obj.Partial;
|
||||
import de.tudbut.timer.AsyncTask;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Fully compatible, fast version of java HTTP requests
|
||||
*/
|
||||
public class HTTPRequest {
|
||||
public final ArrayList<HTTPHeader> headers = new ArrayList<>();
|
||||
public final String content;
|
||||
private final HTTPRequestType requestType;
|
||||
private final String path;
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final boolean ssl;
|
||||
|
||||
/**
|
||||
* Constructs a HTTPRequest without sending it
|
||||
* @param requestType The type of the request, see {@link HTTPRequestType}.
|
||||
* @param host Host, add "https://" in front to attempt an HTTPS connection, otherwise, dont specify protocol
|
||||
* @param port Port, 80 is standard for HTTP, 443 is standard for HTTPS
|
||||
* @param path Path to request, use "/" for main path
|
||||
* @param headers HTTPHeaders to use, can be empty
|
||||
*/
|
||||
public HTTPRequest(HTTPRequestType requestType, String host, int port, String path, HTTPHeader... headers) {
|
||||
this(requestType, host, port, path, (String) null, "", headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a HTTPRequest without sending it
|
||||
* @param requestTypeIn The type of the request, see {@link HTTPRequestType}.
|
||||
* @param hostIn Host, add "https://" in front to attempt a HTTPS connection, otherwise, dont specify protocol
|
||||
* @param portIn Port, 80 is standard for HTTP, 443 is standard for HTTPS
|
||||
* @param pathIn Path to request, use "/" for main path
|
||||
* @param type The type of the body
|
||||
* @param contentIn The body
|
||||
* @param headersIn HTTPHeaders to use, can be empty
|
||||
*/
|
||||
public HTTPRequest(HTTPRequestType requestTypeIn, String hostIn, int portIn, String pathIn, HTTPContentType type, String contentIn, HTTPHeader... headersIn) {
|
||||
this(requestTypeIn, hostIn, portIn, pathIn, type.asHeaderString, contentIn, headersIn);
|
||||
}
|
||||
/**
|
||||
* Constructs a HTTPRequest without sending it
|
||||
* @param requestTypeIn The type of the request, see {@link HTTPRequestType}.
|
||||
* @param hostIn Host, add "https://" in front to attempt a HTTPS connection, otherwise, dont specify protocol
|
||||
* @param portIn Port, 80 is standard for HTTP, 443 is standard for HTTPS
|
||||
* @param pathIn Path to request, use "/" for main path
|
||||
* @param type The type of the body
|
||||
* @param contentIn The body
|
||||
* @param headersIn HTTPHeaders to use, can be empty
|
||||
*/
|
||||
public HTTPRequest(HTTPRequestType requestTypeIn, String hostIn, int portIn, String pathIn, String type, String contentIn, HTTPHeader... headersIn) {
|
||||
this(requestTypeIn, hostIn, portIn, pathIn, new HTTPHeader("Content-Type", type), contentIn, headersIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a HTTPRequest without sending it
|
||||
* @param requestTypeIn The type of the request, see {@link HTTPRequestType}.
|
||||
* @param hostIn Host, add "https://" in front to attempt a HTTPS connection, otherwise, dont specify protocol
|
||||
* @param portIn Port, 80 is standard for HTTP, 443 is standard for HTTPS
|
||||
* @param pathIn Path to request, use "/" for main path
|
||||
* @param type The type of the body
|
||||
* @param contentIn The body
|
||||
* @param headersIn HTTPHeaders to use, can be empty
|
||||
*/
|
||||
public HTTPRequest(HTTPRequestType requestTypeIn, String hostIn, int portIn, String pathIn, HTTPHeader type, String contentIn, HTTPHeader... headersIn) {
|
||||
ssl = hostIn.startsWith("https://");
|
||||
|
||||
requestType = requestTypeIn;
|
||||
path = pathIn;
|
||||
host = hostIn;
|
||||
port = portIn;
|
||||
headers.add(new HTTPHeader("Host", ssl ? host.split("https://")[1] : host));
|
||||
if(!contentIn.equals("")) {
|
||||
headers.add(new HTTPHeader("Content-Type", type.value(), type.parameter()));
|
||||
headers.add(new HTTPHeader("Content-Length", String.valueOf(contentIn.getBytes(StandardCharsets.ISO_8859_1).length)));
|
||||
}
|
||||
if(Arrays.stream(headersIn).noneMatch(httpHeader -> httpHeader.toString().startsWith("Connection: ")))
|
||||
headers.add(new HTTPHeader("Connection", "Close"));
|
||||
headers.addAll(Arrays.asList(headersIn));
|
||||
content = contentIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string to send to the server
|
||||
*/
|
||||
public String toString() {
|
||||
String s = "";
|
||||
|
||||
s += requestType.name() + " " + path + " HTTP/1.1\r\n";
|
||||
for (HTTPHeader header : headers) {
|
||||
s += header.toString() + "\r\n";
|
||||
}
|
||||
s += "\r\n";
|
||||
s += content;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the request synchronously
|
||||
* @return The response
|
||||
* @throws IOException Inherited
|
||||
*/
|
||||
public HTTPResponse send() throws IOException {
|
||||
Socket socket = connect();
|
||||
return new HTTPResponse(new String(new StreamReader(socket.getInputStream()).readAllAsBytes(), StandardCharsets.ISO_8859_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the request synchronously without returning a response
|
||||
* @return The socket
|
||||
* @throws IOException Inherited
|
||||
*/
|
||||
public Socket sendNoRead() throws IOException {
|
||||
return connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the request asynchronously
|
||||
* @return The response enclosed in a {@link Partial}
|
||||
*/
|
||||
public Partial<HTTPResponse> sendKeepAlive() {
|
||||
return sendKeepAlive(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the request asynchronously with optional timeout
|
||||
* @param timeout The timeout at which to stop receiving, use -1 for infinity
|
||||
* @return The response enclosed in a {@link Partial}
|
||||
*/
|
||||
public Partial<HTTPResponse> sendKeepAlive(int timeout) {
|
||||
Partial<HTTPResponse> partialResponse = new Partial<>(null);
|
||||
AsyncTask<HTTPResponse> task = new AsyncTask<>(() -> {
|
||||
try {
|
||||
Socket socket = connect();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.ISO_8859_1));
|
||||
int c;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while ((c = reader.read()) != -1) {
|
||||
builder.append((char)c);
|
||||
if(c == '\n')
|
||||
partialResponse.change(new HTTPResponse(builder.toString()));
|
||||
}
|
||||
socket.close();
|
||||
partialResponse.change(new HTTPResponse(builder.toString()));
|
||||
partialResponse.complete(partialResponse.get());
|
||||
} catch (Exception ignored) { }
|
||||
return partialResponse.get();
|
||||
});
|
||||
task.setTimeout(timeout);
|
||||
return partialResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the request asynchronously with optional timeout
|
||||
* @param timeout The timeout at which to stop receiving, use -1 for infinity
|
||||
* @return The task and enclosed response (in a {@link Partial}), enclosed in a {@link DoubleTypedObject}
|
||||
*/
|
||||
public DoubleTypedObject<Partial<HTTPResponse>, AsyncTask<HTTPResponse>> sendKeepAliveWithTask(int timeout) {
|
||||
Partial<HTTPResponse> partialResponse = new Partial<>(null);
|
||||
AsyncTask<HTTPResponse> task = new AsyncTask<>(() -> {
|
||||
try {
|
||||
Socket socket = connect();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.ISO_8859_1));
|
||||
int c;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while ((c = reader.read()) != -1) {
|
||||
builder.append((char)c);
|
||||
if(c == '\n')
|
||||
partialResponse.change(new HTTPResponse(builder.toString()));
|
||||
}
|
||||
socket.close();
|
||||
partialResponse.change(new HTTPResponse(builder.toString()));
|
||||
partialResponse.complete(partialResponse.get());
|
||||
return partialResponse.get();
|
||||
} catch (Exception ignored) { }
|
||||
return partialResponse.get();
|
||||
});
|
||||
task.setTimeout(timeout);
|
||||
return new DoubleTypedObject<>(partialResponse, task);
|
||||
}
|
||||
/**
|
||||
* Sends the request and return the socket from which {@link #send} and {@link #sendKeepAlive} will read
|
||||
* @return The created socket
|
||||
* @throws IOException Inherited
|
||||
*/
|
||||
private Socket connect() throws IOException {
|
||||
Socket socket;
|
||||
if (ssl) {
|
||||
socket = new Socket(host.substring("https://".length()), port);
|
||||
SSLSocket sslSocket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(socket, host.substring("https://".length()), port, true);
|
||||
sslSocket.startHandshake();
|
||||
if(DebugStateManager.isDebugEnabled()) {
|
||||
DebugStateManager.getDebugLogger().debug("HTTPRequest using SSL/TLS version " + sslSocket.getSession().getProtocol());
|
||||
}
|
||||
socket = sslSocket;
|
||||
}
|
||||
else
|
||||
socket = new Socket(InetAddress.getByName(host), port);
|
||||
socket.setSoTimeout(10000);
|
||||
socket.setSoLinger(true, 1000);
|
||||
StreamWriter writer = new StreamWriter(socket.getOutputStream());
|
||||
writer.writeChars(toString().toCharArray(), "ISO_8859_1");
|
||||
return socket;
|
||||
}
|
||||
|
||||
}
|
58
src/main/java/de/tudbut/net/http/HTTPRequestType.java
Normal file
58
src/main/java/de/tudbut/net/http/HTTPRequestType.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
/**
|
||||
* Request types
|
||||
*/
|
||||
public enum HTTPRequestType {
|
||||
/**
|
||||
* Get request, default for browsers
|
||||
*/
|
||||
GET,
|
||||
|
||||
/**
|
||||
* Post request, common for APIs
|
||||
*/
|
||||
POST,
|
||||
|
||||
/**
|
||||
* Head request, asks the server to respond with the same headers
|
||||
* as a {@link #GET}, but without content. Commonly used in browsers
|
||||
* that use caching. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD">More</a>
|
||||
*/
|
||||
HEAD,
|
||||
|
||||
/**
|
||||
* Put request, used for writing files to a server
|
||||
*/
|
||||
PUT,
|
||||
|
||||
/**
|
||||
* Patch request, used for changing files on a server.
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH">More</a>
|
||||
*/
|
||||
PATCH,
|
||||
|
||||
/**
|
||||
* Delete request, used for deleting files on a server.
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE">More</a>
|
||||
*/
|
||||
DELETE,
|
||||
|
||||
/**
|
||||
* Trace request, used for debugging
|
||||
*/
|
||||
TRACE,
|
||||
|
||||
/**
|
||||
* Options request, used for asking the server for allowed communication methods.
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS">More</a>
|
||||
*/
|
||||
OPTIONS,
|
||||
|
||||
/**
|
||||
* Connect request, used to indicate start of a two-way transfer.
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT">More</a>
|
||||
*/
|
||||
CONNECT,
|
||||
;
|
||||
}
|
148
src/main/java/de/tudbut/net/http/HTTPResponse.java
Normal file
148
src/main/java/de/tudbut/net/http/HTTPResponse.java
Normal file
|
@ -0,0 +1,148 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
import de.tudbut.tools.Value;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Response to {@link HTTPRequest}
|
||||
*/
|
||||
public class HTTPResponse extends Value<String> {
|
||||
|
||||
/**
|
||||
* Constructs a HTTPResponse from non-exact content
|
||||
* @param value The content
|
||||
*/
|
||||
public HTTPResponse(String value) {
|
||||
super(spl(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a HTTPResponse from content
|
||||
* @param value The content
|
||||
*/
|
||||
public HTTPResponse(String value, boolean exact) {
|
||||
super(exact ? value : spl(value));
|
||||
}
|
||||
|
||||
private static String spl(String s) {
|
||||
String[] splitString = s.split("\r\n\r\n", 2);
|
||||
if(splitString.length == 1) {
|
||||
return s.replaceAll("\r", "");
|
||||
}
|
||||
else {
|
||||
return splitString[0].replaceAll("\r", "") + "\n\n" + splitString[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the response
|
||||
* @return The {@link ParsedHTTPValue} of the response
|
||||
*/
|
||||
public ParsedHTTPValue parse() {
|
||||
String[] splitValue = value.split("\n", 2)[0].split(" ");
|
||||
|
||||
String httpVersion = splitValue[0];
|
||||
int statusCode = Integer.parseInt(splitValue[1]);
|
||||
String statusCodeString = splitValue[2];
|
||||
HTTPResponseCode code = null;
|
||||
for (int i = 0 ; i < HTTPResponseCode.values().length; i++) {
|
||||
HTTPResponseCode responseCode = HTTPResponseCode.values()[i];
|
||||
if(responseCode.asInt == statusCode) {
|
||||
code = responseCode;
|
||||
}
|
||||
}
|
||||
ArrayList<HTTPHeader> headersList = new ArrayList<>();
|
||||
String s = value;
|
||||
s = s.substring(s.split("\n")[0].length() + 1);
|
||||
for (String line : s.split("\n")) {
|
||||
if (line.equals(""))
|
||||
break;
|
||||
headersList.add(new HTTPHeader(line));
|
||||
}
|
||||
HTTPHeader[] headers = headersList.toArray(new HTTPHeader[0]);
|
||||
StringBuilder body = new StringBuilder();
|
||||
try {
|
||||
int start = value.indexOf("\n\n") + 2;
|
||||
HTTPHeader header = null;
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
if(headers[i].key().equalsIgnoreCase("Content-Length"))
|
||||
header = headers[i];
|
||||
}
|
||||
if(header != null) {
|
||||
int end = value.length();
|
||||
body = new StringBuilder(value.substring(start, end));
|
||||
}
|
||||
else {
|
||||
|
||||
/*
|
||||
* INCREDIBLY hacky way to make chunk transfer work, will make better later
|
||||
*/
|
||||
ByteArrayInputStream b = new ByteArrayInputStream(value.substring(start).getBytes(StandardCharsets.ISO_8859_1));
|
||||
|
||||
for (int i = -1 ; i != 0 ; ) {
|
||||
StringBuilder sbuf = new StringBuilder();
|
||||
int c;
|
||||
while (!sbuf.toString().endsWith("\n") && (c = b.read()) != -1) {
|
||||
sbuf.append((char) c);
|
||||
}
|
||||
i = Integer.parseInt(sbuf.toString().replaceAll("\r", "").split("\n")[0], 16);
|
||||
byte[] buf = new byte[i];
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
b.read(buf);
|
||||
stream.write(buf);
|
||||
body.append(stream.toString());
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
HTTPResponseCode finalCode = code;
|
||||
String finalBody = body.toString();
|
||||
return new ParsedHTTPValue() {
|
||||
@Override
|
||||
public String getHTTPVersion() {
|
||||
return httpVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusCodeAsString() {
|
||||
return statusCodeString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getQuery() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPResponseCode getStatusCodeAsEnum() {
|
||||
return finalCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBodyRaw() {
|
||||
return finalBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPHeader[] getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
84
src/main/java/de/tudbut/net/http/HTTPResponseCode.java
Normal file
84
src/main/java/de/tudbut/net/http/HTTPResponseCode.java
Normal file
|
@ -0,0 +1,84 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
/**
|
||||
* Response codes to HTTPRequests
|
||||
*/
|
||||
public enum HTTPResponseCode {
|
||||
Continue(100),
|
||||
SwitchingProtocols(101),
|
||||
Processing(102),
|
||||
EarlyHints(103),
|
||||
|
||||
OK(200),
|
||||
Created(201),
|
||||
Accepted(202),
|
||||
NonAuthoritativeInformation(203),
|
||||
NoContent(204),
|
||||
ResetContent(205),
|
||||
PartialContent(206),
|
||||
MultiStatus(207),
|
||||
AlreadyReported(208),
|
||||
IM_Used(226),
|
||||
|
||||
MultipleChoices(300),
|
||||
MovedPermanently(301),
|
||||
MovedTemporarily(302),
|
||||
SeeOther(303),
|
||||
NotModified(304),
|
||||
UseProxy(305),
|
||||
RESERVED__SwitchProxy(306),
|
||||
TemporaryRedirect(307),
|
||||
PermanentRedirect(308),
|
||||
|
||||
BadRequest(400),
|
||||
Unauthorized(401),
|
||||
PaymentRequired(402),
|
||||
Forbidden(403),
|
||||
NotFound(404),
|
||||
MethodNotAllowed(405),
|
||||
NotAcceptable(406),
|
||||
ProxyAuthenticationRequired(407),
|
||||
RequestTimeout(408),
|
||||
Conflict(409),
|
||||
Gone(410),
|
||||
LengthRequired(411),
|
||||
PreconditionFailed(412),
|
||||
PayloadTooLarge(413),
|
||||
URI_TooLong(414),
|
||||
UnsupportedMediaType(415),
|
||||
RangeNotSatisfiable(416),
|
||||
ExpectationFailed(417),
|
||||
MisdirectedRequest(421),
|
||||
UnprocessableEntity(422),
|
||||
Locked(423),
|
||||
FailedDependency(424),
|
||||
TooEarly(425),
|
||||
UpgradeRequired(426),
|
||||
PreconditionRequired(428),
|
||||
TooManyRequests(429),
|
||||
RequestHeaderFieldsTooLarge(431),
|
||||
UnavailableForLegalReasons(451),
|
||||
|
||||
InternalServerError(500),
|
||||
NotImplemented(501),
|
||||
BadGateway(502),
|
||||
ServiceUnavailable(503),
|
||||
GatewayTimeout(504),
|
||||
HTTPVersionNotSupported(505),
|
||||
VariantAlsoNegotiates(506),
|
||||
InsufficientStorage(507),
|
||||
LoopDetected(508),
|
||||
BandwidthLimitExceeded(509),
|
||||
NotExtended(510),
|
||||
NetworkAuthenticationRequired(511),
|
||||
;
|
||||
|
||||
/**
|
||||
* The id
|
||||
*/
|
||||
public final int asInt;
|
||||
|
||||
HTTPResponseCode(int asIntIn) {
|
||||
asInt = asIntIn;
|
||||
}
|
||||
}
|
55
src/main/java/de/tudbut/net/http/HTTPResponseFactory.java
Normal file
55
src/main/java/de/tudbut/net/http/HTTPResponseFactory.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Class to build HTTP responses
|
||||
*/
|
||||
public class HTTPResponseFactory {
|
||||
|
||||
/**
|
||||
* Creates a HTTPResponse
|
||||
* @param responseCode {@link HTTPResponseCode}
|
||||
* @param body The body of the request
|
||||
* @param contentType {@link HTTPContentType}
|
||||
* @param headers {@link HTTPHeader} Headers for the response
|
||||
* @return The constructed {@link HTTPResponse}
|
||||
*/
|
||||
public static HTTPResponse create(HTTPResponseCode responseCode, String body, HTTPContentType contentType, HTTPHeader... headers) {
|
||||
return create(responseCode, body, contentType.asHeaderString, headers);
|
||||
}
|
||||
/**
|
||||
* Creates a HTTPResponse
|
||||
* @param responseCode {@link HTTPResponseCode}
|
||||
* @param body The body of the request
|
||||
* @param contentType {@link HTTPContentType}
|
||||
* @param headers {@link HTTPHeader} Headers for the response
|
||||
* @return The constructed {@link HTTPResponse}
|
||||
*/
|
||||
public static HTTPResponse create(HTTPResponseCode responseCode, String body, String contentType, HTTPHeader... headers) {
|
||||
return create(responseCode, body, new HTTPHeader("Content-Type", contentType), headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HTTPResponse
|
||||
* @param responseCode {@link HTTPResponseCode}
|
||||
* @param body The body of the request
|
||||
* @param contentType content type
|
||||
* @param headers {@link HTTPHeader} Headers for the response
|
||||
* @return The constructed {@link HTTPResponse}
|
||||
*/
|
||||
public static HTTPResponse create(HTTPResponseCode responseCode, String body, HTTPHeader contentType, HTTPHeader... headers) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("HTTP/1.1 ").append(responseCode.asInt).append(" ").append(responseCode.name()).append("\r\n");
|
||||
builder.append(new HTTPHeader("Content-Type", contentType.value(), contentType.parameter())).append("\r\n");
|
||||
builder.append(new HTTPHeader("Content-Length", body.getBytes(StandardCharsets.ISO_8859_1).length + "")).append("\r\n");
|
||||
for (HTTPHeader header : headers) {
|
||||
builder.append(header.toString()).append("\r\n");
|
||||
}
|
||||
builder.append("\r\n");
|
||||
builder.append(body);
|
||||
|
||||
return new HTTPResponse(builder.toString(), true);
|
||||
}
|
||||
}
|
213
src/main/java/de/tudbut/net/http/HTTPServer.java
Normal file
213
src/main/java/de/tudbut/net/http/HTTPServer.java
Normal file
|
@ -0,0 +1,213 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
import de.tudbut.io.RawLineReader;
|
||||
import de.tudbut.io.StreamWriter;
|
||||
import de.tudbut.type.Stoppable;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.security.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
// TODO: this has a broken thing, please dont use for HTTPS until later.
|
||||
|
||||
/**
|
||||
* A HTTP server
|
||||
*/
|
||||
public class HTTPServer implements Stoppable {
|
||||
private final int port;
|
||||
private final ServerSocket serverSocket;
|
||||
private final HTTPResponse serverError;
|
||||
private final ArrayList<HTTPHandler> handlers = new ArrayList<>();
|
||||
private final Executor executor;
|
||||
|
||||
/**
|
||||
* Constructs a HTTPServer without HTTPS
|
||||
* @param portIn Port to listen on
|
||||
* @throws IOException Inherited
|
||||
*/
|
||||
public HTTPServer(int portIn) throws IOException {
|
||||
port = portIn;
|
||||
serverError = HTTPResponseFactory.create(HTTPResponseCode.InternalServerError, "500 InternalServerError", HTTPContentType.TXT);
|
||||
serverSocket = new ServerSocket(port);
|
||||
executor = Runnable::run;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a HTTPServer without HTTPS
|
||||
* @param portIn Port to listen on
|
||||
* @param serverErrorIn Response to send on error
|
||||
* @param executorIn Executor
|
||||
* @throws IOException Inherited
|
||||
*/
|
||||
public HTTPServer(int portIn, HTTPResponse serverErrorIn, Executor executorIn) throws IOException {
|
||||
port = portIn;
|
||||
serverError = serverErrorIn;
|
||||
serverSocket = new ServerSocket(port);
|
||||
executor = executorIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a HTTPServer with HTTPS
|
||||
* @param portIn Port to listen on
|
||||
* @param serverErrorIn Response to send on error
|
||||
* @param executorIn Executor
|
||||
* @param keyStore {@link KeyStore} The PRIVATE KEY KeyStore
|
||||
* @param keyStorePass Password for the KeyStore
|
||||
* @throws IOException Inherited
|
||||
*/
|
||||
public HTTPServer(int portIn, HTTPResponse serverErrorIn, Executor executorIn, KeyStore keyStore, String keyStorePass) throws IOException {
|
||||
port = portIn;
|
||||
serverError = serverErrorIn;
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
KeyManagerFactory managerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
managerFactory.init(keyStore, keyStorePass.toCharArray());
|
||||
context.init(managerFactory.getKeyManagers(), null, null);
|
||||
serverSocket = context.getServerSocketFactory().createServerSocket(portIn);
|
||||
}
|
||||
catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException | KeyManagementException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
executor = executorIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening for requests
|
||||
*/
|
||||
public void listen() {
|
||||
new Thread(() -> {
|
||||
Socket socket;
|
||||
while (!isStopped()) {
|
||||
try {
|
||||
socket = serverSocket.accept();
|
||||
socket.setSoTimeout(10000);
|
||||
Socket finalSocket = socket;
|
||||
boolean b = true;
|
||||
for (int i = 0 ; i < handlers.size() ; i++) {
|
||||
if(!handlers.get(i).accept(socket.getRemoteSocketAddress()) || !handlers.get(i).accept(socket, socket.getRemoteSocketAddress()))
|
||||
b = false;
|
||||
}
|
||||
if(b) {
|
||||
executor.execute(() -> {
|
||||
HTTPServerRequest serverRequest = new HTTPServerRequest("", finalSocket);
|
||||
try {
|
||||
HTTPHandler[] handlers = this.handlers.toArray(new HTTPHandler[0]);
|
||||
|
||||
String s;
|
||||
ArrayList<HTTPHeader> headers = new ArrayList<>();
|
||||
String fullRequest = "";
|
||||
RawLineReader reader = new RawLineReader(finalSocket.getInputStream());
|
||||
int line = 0;
|
||||
while ((s = reader.readLine()) != null) {
|
||||
fullRequest += s + "\r\n";
|
||||
if (s.equals("")) {
|
||||
break;
|
||||
}
|
||||
if (line != 0) {
|
||||
headers.add(new HTTPHeader(s));
|
||||
}
|
||||
line++;
|
||||
}
|
||||
int contentLength = 0;
|
||||
for (HTTPHeader header : headers) {
|
||||
if (header.key().equalsIgnoreCase("Content-Length")) {
|
||||
contentLength = Integer.parseInt(header.value());
|
||||
}
|
||||
}
|
||||
if(contentLength != 0) {
|
||||
for (int i = 0 ; i < contentLength ; i++) {
|
||||
fullRequest += (char) reader.read();
|
||||
}
|
||||
}
|
||||
HTTPServerRequest request = new HTTPServerRequest(fullRequest, finalSocket);
|
||||
serverRequest = request;
|
||||
for (HTTPHandler handler : handlers) {
|
||||
handler.handle(request);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
try {
|
||||
new StreamWriter(finalSocket.getOutputStream()).writeChars(handlers.get(0).onError(serverRequest, serverError, e).value.toCharArray(), "ISO-8859-1");
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
executor.execute(() -> {
|
||||
for (int i = 0 ; i < handlers.size() ; i++) {
|
||||
try {
|
||||
handlers.get(i).handleDeny(new HTTPServerRequest("", finalSocket));
|
||||
}
|
||||
catch (Exception ignored) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException ignore) { }
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a handler to handle requests
|
||||
* @param handler The handler to add
|
||||
*/
|
||||
public void addHandler(HTTPHandler handler) {
|
||||
handlers.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for incoming HTTP request
|
||||
*/
|
||||
public interface HTTPHandler {
|
||||
/**
|
||||
* Handle a request to the server
|
||||
* @param request The request to handle
|
||||
* @throws Exception To make handling easier (less catching required)
|
||||
*/
|
||||
void handle(HTTPServerRequest request) throws Exception;
|
||||
|
||||
/**
|
||||
* Should the connection be accepted? (Useful for rate-limiting)
|
||||
* @param address The address the connection is coming from
|
||||
* @return If the connection should be accepted
|
||||
*/
|
||||
default boolean accept(SocketAddress address) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Should the connection be accepted? (Useful for rate-limiting)
|
||||
* @param socket The socket
|
||||
* @param address The address the connection is coming from
|
||||
* @return If the connection should be accepted
|
||||
*/
|
||||
default boolean accept(Socket socket, SocketAddress address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param request An empty request to hold the response methods
|
||||
* @throws Exception To make handling easier (less catching required)
|
||||
*/
|
||||
default void handleDeny(HTTPServerRequest request) throws Exception { }
|
||||
|
||||
/**
|
||||
* @param request The request as constructed so far
|
||||
* @param defaultResponse The server's default response
|
||||
* @param theError The error
|
||||
* @throws Exception To make handling easier (less catching required)
|
||||
*/
|
||||
default HTTPResponse onError(HTTPServerRequest request, HTTPResponse defaultResponse, Throwable theError) throws Exception {
|
||||
return defaultResponse;
|
||||
}
|
||||
}
|
||||
}
|
221
src/main/java/de/tudbut/net/http/HTTPServerRequest.java
Normal file
221
src/main/java/de/tudbut/net/http/HTTPServerRequest.java
Normal file
|
@ -0,0 +1,221 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
import de.tudbut.io.StreamWriter;
|
||||
import de.tudbut.obj.Closable;
|
||||
import de.tudbut.obj.ClosedClosableException;
|
||||
import de.tudbut.obj.NotSupportedException;
|
||||
import de.tudbut.tools.Value;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used by the server to handle incoming HTTP requests
|
||||
*/
|
||||
public class HTTPServerRequest extends Value<String> implements Closable {
|
||||
/**
|
||||
* The socket of the request
|
||||
*/
|
||||
public final Socket socket;
|
||||
|
||||
public HTTPServerRequest(String value, Socket s) {
|
||||
super(spl(value));
|
||||
socket = s;
|
||||
}
|
||||
|
||||
private static String spl(String s) {
|
||||
String[] splitString = s.split("\r\n\r\n", 2);
|
||||
if(splitString.length == 1) {
|
||||
return s.replaceAll("\r", "");
|
||||
}
|
||||
else {
|
||||
return splitString[0].replaceAll("\r", "") + "\n\n" + splitString[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the streams
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
Closable.super.close();
|
||||
socket.shutdownOutput();
|
||||
if(socket instanceof SSLSocket) {
|
||||
socket.setSoLinger(true, 100);
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
try {
|
||||
SSLSocket.class.getMethod("close").invoke(socket);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
System.err.println("!!! ERROR: OBFUSCATED OR INVALID JRE");
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
else
|
||||
socket.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Not supported.
|
||||
* @throws NotSupportedException [Always thrown]
|
||||
*/
|
||||
@Override
|
||||
public void open() throws NotSupportedException {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to the request. Returns if the request was already responded to.
|
||||
* @param response The response to send
|
||||
* @throws IOException Inherited
|
||||
*/
|
||||
public void respond(HTTPResponse response) throws IOException {
|
||||
if(isClosed())
|
||||
return;
|
||||
StreamWriter writer = new StreamWriter(socket.getOutputStream());
|
||||
writer.writeChars(response.value.toCharArray(), "ISO_8859_1");
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to the request. Throws if the request was already responded to.
|
||||
* @param response The response to send
|
||||
* @throws IOException Inherited
|
||||
* @throws ClosedClosableException If the request has already been responded to.
|
||||
*/
|
||||
public void forceRespond(HTTPResponse response) throws IOException, ClosedClosableException {
|
||||
if(isClosed())
|
||||
throw new ClosedClosableException();
|
||||
StreamWriter writer = new StreamWriter(socket.getOutputStream());
|
||||
writer.writeChars(response.value.toCharArray(), "ISO_8859_1");
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the request
|
||||
* @return The {@link ParsedHTTPValue} of the request
|
||||
*/
|
||||
public ParsedHTTPValue parse() {
|
||||
String[] splitValue = value.split("\n")[0].split(" ");
|
||||
|
||||
String httpVersion = splitValue[2];
|
||||
String statusCode = splitValue[0];
|
||||
String actualPath = splitValue[1];
|
||||
String path = actualPath.split("\\?")[0];
|
||||
Map<String, String> map = new HashMap<>();
|
||||
try {
|
||||
String query = actualPath.split("\\?")[1];
|
||||
|
||||
String[] splitByAnd = query.split("&");
|
||||
for (int i = 0; i < splitByAnd.length; i++) {
|
||||
try {
|
||||
String[] v = splitByAnd[i].split("=");
|
||||
map.put(HTTPUtils.decodeUTF8(v[0]), HTTPUtils.decodeUTF8(v[1]));
|
||||
} catch (Exception ignore) { }
|
||||
}
|
||||
} catch (Exception ignore) { }
|
||||
HTTPRequestType code = null;
|
||||
for (int i = 0; i < HTTPRequestType.values().length; i++) {
|
||||
HTTPRequestType requestType = HTTPRequestType.values()[i];
|
||||
if(requestType.name().equals(statusCode)) {
|
||||
code = requestType;
|
||||
}
|
||||
}
|
||||
HTTPRequestType finalCode = code;
|
||||
ArrayList<HTTPHeader> headersList = new ArrayList<>();
|
||||
String s = value;
|
||||
s = s.substring(s.split("\n")[0].length() + 1);
|
||||
for (String line : s.split("\n")) {
|
||||
if (line.equals(""))
|
||||
break;
|
||||
headersList.add(new HTTPHeader(line.split(": ")[0], line.split(": ")[1]));
|
||||
}
|
||||
HTTPHeader[] headers = headersList.toArray(new HTTPHeader[0]);
|
||||
String body = "";
|
||||
try {
|
||||
int start = value.indexOf("\n\n") + 2;
|
||||
HTTPHeader header = null;
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
if(headers[i].key().equalsIgnoreCase("Content-Length"))
|
||||
header = headers[i];
|
||||
}
|
||||
if(header != null) {
|
||||
int end = value.length();
|
||||
body = value.substring(start, end);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* INCREDIBLY hacky way to make chunk transfer work, will make better later
|
||||
*/
|
||||
ByteArrayInputStream b = new ByteArrayInputStream(value.substring(start).getBytes(StandardCharsets.ISO_8859_1));
|
||||
|
||||
for (int chunk = 0, i = -1 ; i != 0 ; chunk++) {
|
||||
StringBuilder sbuf = new StringBuilder();
|
||||
int c;
|
||||
while (!sbuf.toString().endsWith("\n") && (c = b.read()) != -1) {
|
||||
sbuf.append((char) c);
|
||||
}
|
||||
i = Integer.parseInt(sbuf.toString().replaceAll("\r", "").split("\n")[0], 16);
|
||||
byte[] buf = new byte[i];
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
b.read(buf);
|
||||
stream.write(buf);
|
||||
body += stream.toString();
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String finalBody = body;
|
||||
return new ParsedHTTPValue() {
|
||||
@Override
|
||||
public String getHTTPVersion() {
|
||||
return httpVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatusCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusCodeAsString() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getQuery() {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPRequestType getStatusCodeAsEnum() {
|
||||
return finalCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBodyRaw() {
|
||||
return finalBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPHeader[] getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
80
src/main/java/de/tudbut/net/http/HTTPUtils.java
Normal file
80
src/main/java/de/tudbut/net/http/HTTPUtils.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
import de.tudbut.obj.DoubleTypedObject;
|
||||
import de.tudbut.obj.TLMap;
|
||||
import de.tudbut.tools.Lock;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Utils for sending/receiving with HTTP
|
||||
*/
|
||||
public class HTTPUtils {
|
||||
public static String encodeUTF8(String s) {
|
||||
try {
|
||||
return URLEncoder.encode(s, String.valueOf(StandardCharsets.UTF_8)).replace("+", "%20");
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("Too old OS");
|
||||
}
|
||||
}
|
||||
public static String decodeUTF8(String s) {
|
||||
try {
|
||||
return URLDecoder.decode(s.replace("+", "%20"), String.valueOf(StandardCharsets.UTF_8));
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("Too old OS");
|
||||
}
|
||||
}
|
||||
|
||||
public static String rawToUtf8(String raw) {
|
||||
return new String(raw.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String utf8ToRaw(String utf8) {
|
||||
return new String(utf8.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
public static HTTPServer.HTTPHandler createRateLimitingHandler(int rps, HTTPServer.HTTPHandler requestHandler) {
|
||||
TLMap<String, DoubleTypedObject<Integer, Lock>> map = new TLMap<>();
|
||||
return new HTTPServer.HTTPHandler() {
|
||||
@Override
|
||||
public void handle(HTTPServerRequest request) throws Exception {
|
||||
requestHandler.handle(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(SocketAddress address) {
|
||||
String ip = ((InetSocketAddress) address).getHostString();
|
||||
DoubleTypedObject<Integer, Lock> user = map.get(ip);
|
||||
if(user == null)
|
||||
map.set(ip, user = new DoubleTypedObject<>(0, new Lock()));
|
||||
user.o++;
|
||||
if (user.t.isLocked()) {
|
||||
if (user.o > rps)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
user.t.lock(1000);
|
||||
user.o = 1;
|
||||
}
|
||||
return requestHandler.accept(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDeny(HTTPServerRequest request) throws Exception {
|
||||
requestHandler.handleDeny(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPResponse onError(HTTPServerRequest request, HTTPResponse defaultResponse, Throwable theError) throws Exception {
|
||||
return requestHandler.onError(request, defaultResponse, theError);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
65
src/main/java/de/tudbut/net/http/ParsedHTTPValue.java
Normal file
65
src/main/java/de/tudbut/net/http/ParsedHTTPValue.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
package de.tudbut.net.http;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link HTTPResponse#parse}
|
||||
* {@link HTTPServerRequest#parse}
|
||||
*/
|
||||
public interface ParsedHTTPValue {
|
||||
|
||||
/**
|
||||
* @return The version used to send the request/response
|
||||
*/
|
||||
String getHTTPVersion();
|
||||
|
||||
/**
|
||||
* @return The status code
|
||||
*/
|
||||
int getStatusCode();
|
||||
|
||||
/**
|
||||
* @return The status code
|
||||
*/
|
||||
String getStatusCodeAsString();
|
||||
|
||||
/**
|
||||
* @return The path. Empty if a response
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* @return The query. Empty if a response
|
||||
*/
|
||||
Map<String, String> getQuery();
|
||||
|
||||
/**
|
||||
* @return The status code
|
||||
*/
|
||||
Object getStatusCodeAsEnum();
|
||||
|
||||
/**
|
||||
* @return The body of the request
|
||||
*/
|
||||
String getBodyRaw();
|
||||
|
||||
/**
|
||||
* @return The body of the request as bytes (raw)
|
||||
*/
|
||||
default byte[] getBodyBytes() {
|
||||
return getBodyRaw().getBytes(StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The body of the request as UTF-8
|
||||
*/
|
||||
default String getBody() {
|
||||
return HTTPUtils.rawToUtf8(getBodyRaw());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The headers of the request
|
||||
*/
|
||||
HTTPHeader[] getHeaders();
|
||||
}
|
14
src/main/java/de/tudbut/net/http/serverimpl/ContentType.java
Normal file
14
src/main/java/de/tudbut/net/http/serverimpl/ContentType.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
package de.tudbut.net.http.serverimpl;
|
||||
|
||||
import de.tudbut.net.http.HTTPContentType;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface ContentType {
|
||||
HTTPContentType value();
|
||||
}
|
292
src/main/java/de/tudbut/net/http/serverimpl/HTTPServerImpl.java
Normal file
292
src/main/java/de/tudbut/net/http/serverimpl/HTTPServerImpl.java
Normal file
|
@ -0,0 +1,292 @@
|
|||
package de.tudbut.net.http.serverimpl;
|
||||
|
||||
import de.tudbut.net.http.*;
|
||||
import de.tudbut.obj.TLMap;
|
||||
import de.tudbut.tools.ReflectUtil;
|
||||
import de.tudbut.tools.Tools;
|
||||
import de.tudbut.net.http.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
import static de.tudbut.net.http.serverimpl.Method.from;
|
||||
|
||||
public class HTTPServerImpl implements HTTPServer.HTTPHandler {
|
||||
|
||||
ArrayList<Object> listeners = new ArrayList<>();
|
||||
TLMap<String, ArrayList<Method>> methods = new TLMap<>();
|
||||
TLMap<Method, Object> methodObjects = new TLMap<>();
|
||||
|
||||
public void addListener(Object listener) {
|
||||
if(!checkValidity(listener))
|
||||
throw new IllegalArgumentException("The listener contains a method that is annotated with @Serve " +
|
||||
"but doesn't have matching arguments! Required: " +
|
||||
"de.tudbut.net.http.HTTPServerRequest, de.tudbut.net.http.ParsedHTTPValue, [etc]"
|
||||
);
|
||||
listeners.add(listener);
|
||||
reindex();
|
||||
}
|
||||
public void removeListener(Object listener) {
|
||||
listeners.remove(listener);
|
||||
reindex();
|
||||
}
|
||||
|
||||
private void reindex() {
|
||||
methods = new TLMap<>();
|
||||
for (int i = 0; i < listeners.size(); i++) {
|
||||
Object listener = listeners.get(i);
|
||||
Class<?> clazz = listener.getClass();
|
||||
Method[] clazzMethods = clazz.getDeclaredMethods();
|
||||
for (int j = 0; j < clazzMethods.length; j++) {
|
||||
Method method = clazzMethods[j];
|
||||
if(method.isAnnotationPresent(Serve.class)) {
|
||||
Serve annotation = method.getAnnotation(Serve.class);
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
if(
|
||||
params[0] != HTTPServerRequest.class ||
|
||||
params[1] != ParsedHTTPValue.class ||
|
||||
checkParams(method, params)
|
||||
)
|
||||
continue;
|
||||
ReflectUtil.forceAccessible(method);
|
||||
ArrayList<Method> list = methods.get(annotation.type().toString().charAt(0) + annotation.value(), ArrayList::new);
|
||||
list.add(method);
|
||||
if(list.size() == 1)
|
||||
methods.set(annotation.type().toString().charAt(0) + annotation.value(), list);
|
||||
methodObjects.set(method, listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkParams(Method method, Class<?>[] params) {
|
||||
Annotation[][] paramAnnotations = method.getParameterAnnotations();
|
||||
boolean gvalid = true;
|
||||
for (int k = 2; k < params.length; k++) {
|
||||
boolean valid = false;
|
||||
for (int l = 0; l < paramAnnotations[k].length; l++) {
|
||||
if(
|
||||
paramAnnotations[k][l].annotationType() == Parameter.class ||
|
||||
paramAnnotations[k][l].annotationType() == Path.class ||
|
||||
paramAnnotations[k][l].annotationType() == Header.class
|
||||
) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
gvalid = gvalid && valid;
|
||||
}
|
||||
return !gvalid;
|
||||
}
|
||||
|
||||
private boolean checkValidity(Object listener) {
|
||||
Class<?> clazz = listener.getClass();
|
||||
Method[] clazzMethods = clazz.getDeclaredMethods();
|
||||
for (int j = 0; j < clazzMethods.length; j++) {
|
||||
Method method = clazzMethods[j];
|
||||
if(method.isAnnotationPresent(Serve.class)) {
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
if(params[0] != HTTPServerRequest.class)
|
||||
return false;
|
||||
if(params[1] != ParsedHTTPValue.class)
|
||||
return false;
|
||||
if(checkParams(method, params))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkRequestMethods(Method method, HTTPRequestType requestType) {
|
||||
HTTPRequestType[] values = HTTPRequestType.values();
|
||||
ArrayList<HTTPRequestType> allowed = new ArrayList<>();
|
||||
for (int i = 0, valuesLength = values.length ; i < valuesLength ; i++) {
|
||||
HTTPRequestType value = values[i];
|
||||
if(method.getDeclaredAnnotation(from(value)) != null) {
|
||||
allowed.add(value);
|
||||
}
|
||||
}
|
||||
return allowed.size() == 0 || allowed.contains(requestType);
|
||||
}
|
||||
|
||||
private Method find(ArrayList<Method> methods, ParsedHTTPValue httpValue) {
|
||||
for (int j = 0 ; j < methods.size() ; j++) {
|
||||
Method val = methods.get(j);
|
||||
if(!checkRequestMethods(val, (HTTPRequestType) httpValue.getStatusCodeAsEnum())) {
|
||||
continue;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HTTPServerRequest request) throws Exception {
|
||||
ParsedHTTPValue httpValue = request.parse();
|
||||
Method method = null;
|
||||
String[] keys = methods.keys().toArray(new String[0]);
|
||||
ArrayList<Method>[] vals = ((ArrayList<Method>[]) methods.values().toArray(new ArrayList[0]));
|
||||
String path = httpValue.getPath();
|
||||
while (!path.equals("/") && path.endsWith("/"))
|
||||
path = path.substring(0, path.length() - 1);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
String key = keys[i].substring(1);
|
||||
String[] splitKey = key.split("/");
|
||||
String[] splitPath = path.split("/");
|
||||
if(!key.endsWith("/*") && splitKey.length != splitPath.length) {
|
||||
continue;
|
||||
}
|
||||
boolean valid = true;
|
||||
for (int k = 0 ; k < splitKey.length && k < splitPath.length ; k++) {
|
||||
String s = splitKey[k];
|
||||
switch (keys[i].charAt(0)) {
|
||||
case 'W':
|
||||
if (!splitPath[k].matches(Tools.wildcardToRegex(splitKey[k])))
|
||||
valid = false;
|
||||
break;
|
||||
case 'P':
|
||||
if (!splitPath[k].equals(splitKey[k]))
|
||||
valid = false;
|
||||
break;
|
||||
case 'R':
|
||||
if (!splitPath[k].matches(splitKey[k]))
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(valid) {
|
||||
Method m = find(vals[i], httpValue);
|
||||
if(m != null)
|
||||
method = m;
|
||||
}
|
||||
}
|
||||
if(method == null) {
|
||||
method = find(methods.get("404"), httpValue);
|
||||
}
|
||||
Object[] params = createParams(method, httpValue, request);
|
||||
if(params == null) {
|
||||
method = find(methods.get("400"), httpValue);
|
||||
params = new Object[] {request, httpValue};
|
||||
}
|
||||
try {
|
||||
if (Objects.requireNonNull(method).getReturnType() == HTTPResponse.class) {
|
||||
request.respond((HTTPResponse) method.invoke(methodObjects.get(method), params));
|
||||
return;
|
||||
}
|
||||
String s = method.invoke(methodObjects.get(method), params).toString();
|
||||
ContentType contentType = method.getDeclaredAnnotation(ContentType.class);
|
||||
if(contentType == null) {
|
||||
contentType = new ContentType() {
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return ContentType.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPContentType value() {
|
||||
return HTTPContentType.HTML;
|
||||
}
|
||||
};
|
||||
}
|
||||
request.respond(HTTPResponseFactory.create(HTTPResponseCode.OK, s, contentType.value()));
|
||||
} catch (InvocationTargetException exception) {
|
||||
if(exception.getCause() instanceof Response) {
|
||||
request.respond(((Response) exception.getCause()).response);
|
||||
}
|
||||
else
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
private Parameter getParameterAnnotation(Annotation[] annotations) {
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
if(annotations[i].annotationType() == Parameter.class)
|
||||
return (Parameter) annotations[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Path getPathAnnotation(Annotation[] annotations) {
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
if(annotations[i].annotationType() == Path.class)
|
||||
return (Path) annotations[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Header getHeaderAnnotation(Annotation[] annotations) {
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
if(annotations[i].annotationType() == Header.class)
|
||||
return (Header) annotations[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object[] createParams(Method method, ParsedHTTPValue v, HTTPServerRequest r) {
|
||||
try {
|
||||
ArrayList<Object> list = new ArrayList<>();
|
||||
list.add(r);
|
||||
list.add(v);
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
Annotation[][] parameters = method.getParameterAnnotations();
|
||||
String[] splitPath = v.getPath().split("/");
|
||||
for (int i = 2; i < parameterTypes.length; i++) {
|
||||
Parameter param = getParameterAnnotation(parameters[i]);
|
||||
Path path = getPathAnnotation(parameters[i]);
|
||||
Header header = getHeaderAnnotation(parameters[i]);
|
||||
String s = null;
|
||||
if (header != null) {
|
||||
HTTPHeader[] headers = v.getHeaders();
|
||||
for (int j = 0, headersLength = headers.length ; j < headersLength ; j++) {
|
||||
HTTPHeader httpHeader = headers[j];
|
||||
if (httpHeader.key().equalsIgnoreCase(header.value())) {
|
||||
s = httpHeader.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s == null && param != null) {
|
||||
s = v.getQuery().get(param.value());
|
||||
}
|
||||
if (s == null && path != null) {
|
||||
if (splitPath.length > path.value()) {
|
||||
s = splitPath[path.value()];
|
||||
}
|
||||
}
|
||||
if(s == null) {
|
||||
list.add(null);
|
||||
continue;
|
||||
}
|
||||
if(parameterTypes[i] == String.class) {
|
||||
list.add(s);
|
||||
}
|
||||
else if(parameterTypes[i] == Integer.class) {
|
||||
list.add(Integer.parseInt(s));
|
||||
}
|
||||
else if(parameterTypes[i] == Boolean.class) {
|
||||
list.add(!s.equals("0") && !s.equals("false") && !s.equals("null"));
|
||||
}
|
||||
else if(parameterTypes[i] == Long.class) {
|
||||
list.add(Long.parseLong(s));
|
||||
}
|
||||
else if(parameterTypes[i] == Float.class) {
|
||||
list.add(Float.parseFloat(s));
|
||||
}
|
||||
else if(parameterTypes[i] == Double.class) {
|
||||
list.add(Double.parseDouble(s));
|
||||
}
|
||||
else if(parameterTypes[i] == Short.class) {
|
||||
list.add(Short.parseShort(s));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return list.toArray(new Object[0]);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
12
src/main/java/de/tudbut/net/http/serverimpl/Header.java
Normal file
12
src/main/java/de/tudbut/net/http/serverimpl/Header.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package de.tudbut.net.http.serverimpl;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Header {
|
||||
String value();
|
||||
}
|
77
src/main/java/de/tudbut/net/http/serverimpl/Method.java
Normal file
77
src/main/java/de/tudbut/net/http/serverimpl/Method.java
Normal file
|
@ -0,0 +1,77 @@
|
|||
package de.tudbut.net.http.serverimpl;
|
||||
|
||||
import de.tudbut.net.http.HTTPRequestType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
public class Method {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Get {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Post {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Head {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Put {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Patch {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Delete {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Trace {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Options {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Connect {
|
||||
|
||||
}
|
||||
|
||||
public static Class<? extends Annotation> from(HTTPRequestType type) {
|
||||
switch (type) {
|
||||
case GET: return Get.class;
|
||||
case PUT: return Put.class;
|
||||
case HEAD: return Head.class;
|
||||
case POST: return Post.class;
|
||||
case PATCH: return Patch.class;
|
||||
case TRACE: return Trace.class;
|
||||
case DELETE: return Delete.class;
|
||||
case CONNECT: return Connect.class;
|
||||
case OPTIONS: return Options.class;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
12
src/main/java/de/tudbut/net/http/serverimpl/Parameter.java
Normal file
12
src/main/java/de/tudbut/net/http/serverimpl/Parameter.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package de.tudbut.net.http.serverimpl;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Parameter {
|
||||
String value();
|
||||
}
|
13
src/main/java/de/tudbut/net/http/serverimpl/Path.java
Normal file
13
src/main/java/de/tudbut/net/http/serverimpl/Path.java
Normal file
|
@ -0,0 +1,13 @@
|
|||
package de.tudbut.net.http.serverimpl;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Path {
|
||||
|
||||
int value();
|
||||
}
|
15
src/main/java/de/tudbut/net/http/serverimpl/Response.java
Normal file
15
src/main/java/de/tudbut/net/http/serverimpl/Response.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
package de.tudbut.net.http.serverimpl;
|
||||
|
||||
import de.tudbut.net.http.HTTPContentType;
|
||||
import de.tudbut.net.http.HTTPResponse;
|
||||
import de.tudbut.net.http.HTTPResponseCode;
|
||||
import de.tudbut.net.http.HTTPResponseFactory;
|
||||
|
||||
public class Response extends RuntimeException {
|
||||
|
||||
HTTPResponse response;
|
||||
|
||||
public Response(HTTPResponseCode code, String body, HTTPContentType contentType) {
|
||||
response = HTTPResponseFactory.create(code, body, contentType);
|
||||
}
|
||||
}
|
21
src/main/java/de/tudbut/net/http/serverimpl/Serve.java
Normal file
21
src/main/java/de/tudbut/net/http/serverimpl/Serve.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
package de.tudbut.net.http.serverimpl;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Serve {
|
||||
|
||||
String value();
|
||||
Type type() default Type.WILDCARD;
|
||||
|
||||
enum Type {
|
||||
PLAIN,
|
||||
REGEX,
|
||||
WILDCARD,
|
||||
;
|
||||
}
|
||||
}
|
82
src/main/java/de/tudbut/net/ic/Bus.java
Normal file
82
src/main/java/de/tudbut/net/ic/Bus.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
package de.tudbut.net.ic;
|
||||
|
||||
import de.tudbut.tools.Lock;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class Bus implements Closeable { // Work in progress, will comment later!
|
||||
|
||||
final InputStream i;
|
||||
final OutputStream o;
|
||||
final Lock syncI = new Lock(), syncO = new Lock();
|
||||
boolean isClosed;
|
||||
|
||||
public Bus(InputStream i, OutputStream o) {
|
||||
this.i = i;
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
public Bus(Socket socket) throws IOException {
|
||||
this(socket.getInputStream(), socket.getOutputStream());
|
||||
}
|
||||
|
||||
public void write(ByteBuffer buffer) throws IOException {
|
||||
StringBuilder r = new StringBuilder("Written: ");
|
||||
syncO.waitHere();
|
||||
syncO.lock();
|
||||
try {
|
||||
byte[] bytes = buffer.array();
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
o.write(bytes[j]);
|
||||
r.append((char) Byte.toUnsignedInt(bytes[j]));
|
||||
}
|
||||
o.flush();
|
||||
syncO.unlock();
|
||||
} catch (IOException e) {
|
||||
syncO.unlock();
|
||||
throw new IOException(r.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void read(ByteBuffer buffer) throws IOException {
|
||||
StringBuilder r = new StringBuilder("Read: ");
|
||||
syncI.waitHere();
|
||||
syncI.lock();
|
||||
try {
|
||||
byte[] bytes = buffer.array();
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
bytes[j] = (byte) i.read();
|
||||
r.append((char) Byte.toUnsignedInt(bytes[j]));
|
||||
}
|
||||
syncI.unlock();
|
||||
} catch (IOException e) {
|
||||
syncI.unlock();
|
||||
throw new IOException(r.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
i.close();
|
||||
o.close();
|
||||
syncI.unlock();
|
||||
syncO.unlock();
|
||||
isClosed = true;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
if(isClosed)
|
||||
return true;
|
||||
try {
|
||||
i.available();
|
||||
}
|
||||
catch (IOException e) {
|
||||
isClosed = true;
|
||||
}
|
||||
return isClosed;
|
||||
}
|
||||
}
|
272
src/main/java/de/tudbut/net/ic/PBIC.java
Normal file
272
src/main/java/de/tudbut/net/ic/PBIC.java
Normal file
|
@ -0,0 +1,272 @@
|
|||
package de.tudbut.net.ic;
|
||||
|
||||
import de.tudbut.tools.BetterJ;
|
||||
import de.tudbut.tools.Tools;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PBIC {
|
||||
|
||||
public static int getInt(byte[] bytes) {
|
||||
if(bytes.length == 4) {
|
||||
int i = 0;
|
||||
int[] ints = Tools.byteArrayToIntArray(bytes);
|
||||
i += ints[0] << 3 * 8;
|
||||
i += ints[1] << 2 * 8;
|
||||
i += ints[2] << 1 * 8;
|
||||
i += ints[3] << 0 * 8;
|
||||
return i;
|
||||
}
|
||||
else
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
|
||||
public static byte[] putInt(int i) {
|
||||
byte[] bytes = new byte[4];
|
||||
bytes[0] = (byte) ((i >> 3 * 8) & 0xff);
|
||||
bytes[1] = (byte) ((i >> 2 * 8) & 0xff);
|
||||
bytes[2] = (byte) ((i >> 1 * 8) & 0xff);
|
||||
bytes[3] = (byte) ((i >> 0 * 8) & 0xff);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static class Server implements Closeable {
|
||||
private final int port;
|
||||
protected final ServerSocket server;
|
||||
public final ArrayList<Socket> sockets = new ArrayList<>();
|
||||
public final ArrayList<Bus> busses = new ArrayList<>();
|
||||
public final ArrayList<Connection> connections = new ArrayList<>();
|
||||
public final ArrayList<Runnable> onJoin = new ArrayList<>();
|
||||
public Connection lastConnection;
|
||||
|
||||
public Server(int port) throws IOException {
|
||||
this.port = port;
|
||||
server = new ServerSocket();
|
||||
}
|
||||
|
||||
protected void listen() {
|
||||
while (server.isBound() && !server.isClosed()) {
|
||||
try {
|
||||
Socket socket = server.accept();
|
||||
|
||||
sockets.add(socket);
|
||||
Bus bus = new Bus(socket);
|
||||
busses.add(bus);
|
||||
lastConnection = onJoin(socket, bus);
|
||||
Runnable[] array = onJoin.toArray(new Runnable[0]);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
BetterJ.t(array[i]);
|
||||
}
|
||||
Thread.sleep(10);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Connection onJoin(Socket socket, Bus bus) {
|
||||
Connection connection = new Connection() {
|
||||
public Bus getBus() {
|
||||
return bus;
|
||||
}
|
||||
|
||||
public Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
};
|
||||
this.connections.add(connection);
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
server.bind(new InetSocketAddress(port));
|
||||
BetterJ.t(this::listen);
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
int i;
|
||||
for(i = 0; i < this.busses.size(); ++i) {
|
||||
this.busses.get(i).close();
|
||||
}
|
||||
|
||||
for(i = 0; i < this.sockets.size(); ++i) {
|
||||
this.sockets.get(i).close();
|
||||
}
|
||||
|
||||
this.server.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PBICException extends Exception {
|
||||
public PBICException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public PBICException(String message, Exception cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PBICException(Exception cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public static class PBICReadException extends PBICException {
|
||||
|
||||
public PBICReadException(Exception cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
public static class PBICWriteException extends PBICException {
|
||||
|
||||
public PBICWriteException(Exception cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface Connection {
|
||||
Map<Connection, Boolean> oSync = new HashMap<>();
|
||||
Map<Connection, Boolean> iSync = new HashMap<>();
|
||||
|
||||
Bus getBus();
|
||||
|
||||
Socket getSocket();
|
||||
|
||||
|
||||
default boolean isClosed() {
|
||||
return getBus().isClosed() || getSocket().isClosed();
|
||||
}
|
||||
|
||||
default void writePacket(Packet packet) throws PBICException.PBICWriteException {
|
||||
|
||||
while (oSync.getOrDefault(this, false));
|
||||
|
||||
oSync.put(this, true);
|
||||
|
||||
ByteBuffer buffer;
|
||||
|
||||
int length = packet.getLength();
|
||||
byte[] content = packet.getContentBytes();
|
||||
|
||||
try {
|
||||
// Send length
|
||||
buffer = ByteBuffer.allocate(Integer.BYTES);
|
||||
buffer.put(putInt(length));
|
||||
getBus().write(buffer);
|
||||
|
||||
// Send content
|
||||
buffer = ByteBuffer.allocate(length);
|
||||
for (int i = 0; i < content.length; i++) {
|
||||
buffer.put(content[i]);
|
||||
}
|
||||
getBus().write(buffer);
|
||||
} catch (Exception e) {
|
||||
oSync.put(this, false);
|
||||
throw new PBICException.PBICWriteException(e);
|
||||
}
|
||||
|
||||
oSync.put(this, false);
|
||||
}
|
||||
|
||||
default Packet readPacket() throws PBICException.PBICReadException {
|
||||
|
||||
while (iSync.getOrDefault(this, false));
|
||||
|
||||
iSync.put(this, true);
|
||||
|
||||
ByteBuffer buffer;
|
||||
|
||||
int length;
|
||||
byte[] content;
|
||||
|
||||
String strContent;
|
||||
try {
|
||||
// Read length
|
||||
buffer = ByteBuffer.allocate(Integer.BYTES);
|
||||
getBus().read(buffer);
|
||||
length = getInt(buffer.array());
|
||||
|
||||
// Read content
|
||||
buffer = ByteBuffer.allocate(length);
|
||||
getBus().read(buffer);
|
||||
content = buffer.array();
|
||||
|
||||
// Parse content
|
||||
strContent = new String(content);
|
||||
} catch (Exception e) {
|
||||
iSync.put(this, false);
|
||||
throw new PBICException.PBICReadException(e);
|
||||
}
|
||||
|
||||
iSync.put(this, false);
|
||||
|
||||
return new Packet() {
|
||||
@Override
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
return strContent;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public interface Packet {
|
||||
default int getLength() {
|
||||
return getContentBytes().length;
|
||||
}
|
||||
default byte[] getContentBytes() {
|
||||
return getContent().getBytes();
|
||||
}
|
||||
|
||||
String getContent();
|
||||
}
|
||||
|
||||
public static class Client implements Closeable {
|
||||
private final Socket client;
|
||||
public Connection connection;
|
||||
|
||||
public Client(String ip, int port) throws IOException {
|
||||
client = new Socket(ip, port);
|
||||
|
||||
start(new Bus(client));
|
||||
}
|
||||
|
||||
private void start(Bus bus) {
|
||||
connection = new Connection() {
|
||||
@Override
|
||||
public Bus getBus() {
|
||||
return bus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket getSocket() {
|
||||
return client;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
connection.getBus().close();
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
249
src/main/java/de/tudbut/net/nethandler/Packet.java
Normal file
249
src/main/java/de/tudbut/net/nethandler/Packet.java
Normal file
|
@ -0,0 +1,249 @@
|
|||
package de.tudbut.net.nethandler;
|
||||
|
||||
import de.tudbut.net.ws.Connection;
|
||||
import de.tudbut.tools.Tools;
|
||||
import de.tudbut.type.Vector3d;
|
||||
import de.tudbut.global.DebugStateManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@Deprecated
|
||||
public class Packet {
|
||||
private final AtomicInteger id = new AtomicInteger(-1);
|
||||
private final AtomicReference<PacketData> data = new AtomicReference<>();
|
||||
|
||||
public Packet(PacketData data) {
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public Packet() {
|
||||
}
|
||||
|
||||
public static PacketData fromString(String packet) {
|
||||
try {
|
||||
return fromString(packet, null);
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
DebugStateManager.getDebugLogger().debug("Packet couldn't be received. Returning null!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static PacketData fromString(String packet, Connection c) throws IOException {
|
||||
if (packet.startsWith("[BEGIN")) {
|
||||
StringBuilder d = new StringBuilder(packet);
|
||||
|
||||
if (!d.toString().startsWith("[BEGIN(") && !d.toString().endsWith(")["))
|
||||
throw new IllegalStateException("Next data in line is not a splitPacket!");
|
||||
|
||||
d = new StringBuilder();
|
||||
while (!d.toString().endsWith("]END]")) {
|
||||
d.append(c.receive());
|
||||
}
|
||||
|
||||
d = new StringBuilder(d.substring(0, d.length() - "]END]".length()));
|
||||
|
||||
return fromString(d.toString());
|
||||
}
|
||||
|
||||
|
||||
Map<String, String> map = Tools.stringToMap(packet);
|
||||
|
||||
return new PacketData() {
|
||||
public int getID() {
|
||||
return Integer.parseInt(map.get("id"));
|
||||
}
|
||||
|
||||
public String getString0() {
|
||||
return map.get("s0");
|
||||
}
|
||||
|
||||
public String getString1() {
|
||||
return map.get("s1");
|
||||
}
|
||||
|
||||
public int getInt0() {
|
||||
return Integer.parseInt(map.getOrDefault("i0", "0"));
|
||||
}
|
||||
|
||||
public int getInt1() {
|
||||
return Integer.parseInt(map.getOrDefault("i1", "0"));
|
||||
}
|
||||
|
||||
public long getLong0() {
|
||||
return Long.parseLong(map.getOrDefault("l0", "0"));
|
||||
}
|
||||
|
||||
public long getLong1() {
|
||||
return Long.parseLong(map.getOrDefault("l1", "0"));
|
||||
}
|
||||
|
||||
public Map<String, String> getMap0() {
|
||||
return Tools.stringToMap(map.get("m0"));
|
||||
}
|
||||
|
||||
public Map<String, String> getMap1() {
|
||||
return Tools.stringToMap(map.get("m1"));
|
||||
}
|
||||
|
||||
public double getDouble0() {
|
||||
return Double.parseDouble(map.getOrDefault("d0", "0"));
|
||||
}
|
||||
|
||||
public double getDouble1() {
|
||||
return Double.parseDouble(map.getOrDefault("d1", "0"));
|
||||
}
|
||||
|
||||
public float getFloat0() {
|
||||
return Float.parseFloat(map.getOrDefault("f0", "0"));
|
||||
}
|
||||
|
||||
public float getFloat1() {
|
||||
return Float.parseFloat(map.getOrDefault("f1", "0"));
|
||||
}
|
||||
|
||||
public Vector3d getVector0() {
|
||||
return new Vector3d(Double.parseDouble(Tools.stringToMap(map.get("v0")).get("x")), Double.parseDouble(Tools.stringToMap(map.get("v0")).get("y")), Double.parseDouble(Tools.stringToMap(map.get("v0")).get("z")));
|
||||
}
|
||||
|
||||
public Vector3d getVector1() {
|
||||
return new Vector3d(Double.parseDouble(Tools.stringToMap(map.get("v1")).get("x")), Double.parseDouble(Tools.stringToMap(map.get("v1")).get("y")), Double.parseDouble(Tools.stringToMap(map.get("v1")).get("z")));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static PacketData receivePieces(Connection c) throws IOException {
|
||||
try {
|
||||
StringBuilder d = new StringBuilder(c.receive());
|
||||
|
||||
if (!d.toString().startsWith("[BEGIN(") && !d.toString().endsWith(")["))
|
||||
throw new IllegalStateException("Next data in line is not a splitPacket!");
|
||||
|
||||
int bytesPerPiece = Integer.parseInt(d.toString().split("\\(")[1].split("\\)")[0]);
|
||||
|
||||
d = new StringBuilder();
|
||||
while (!d.toString().endsWith("]END]")) {
|
||||
d.append(c.receive());
|
||||
}
|
||||
|
||||
d = new StringBuilder(d.substring(0, d.length() - "]END]".length()));
|
||||
|
||||
return fromString(d.toString());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Next data in line is not a splitPacket!");
|
||||
}
|
||||
}
|
||||
|
||||
public void send(Connection connection) throws IOException {
|
||||
connection.send(this.toString());
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return id.get();
|
||||
}
|
||||
|
||||
public void setID(int i) {
|
||||
id.set(i);
|
||||
}
|
||||
|
||||
public <T> T getDataValue(PacketDataID id) {
|
||||
switch (id) {
|
||||
case ID:
|
||||
return (T) new Integer(getData().getID());
|
||||
case INT0:
|
||||
return (T) new Integer(getData().getInt0());
|
||||
case INT1:
|
||||
return (T) new Integer(getData().getInt1());
|
||||
case STRING0:
|
||||
return (T) getData().getString0();
|
||||
case STRING1:
|
||||
return (T) getData().getString1();
|
||||
case LONG0:
|
||||
return (T) new Long(getData().getLong0());
|
||||
case LONG1:
|
||||
return (T) new Long(getData().getLong1());
|
||||
case DOUBLE0:
|
||||
return (T) new Double(getData().getDouble0());
|
||||
case DOUBLE1:
|
||||
return (T) new Double(getData().getDouble1());
|
||||
case FLOAT0:
|
||||
return (T) new Float(getData().getFloat0());
|
||||
case FLOAT1:
|
||||
return (T) new Float(getData().getFloat1());
|
||||
case MAP0:
|
||||
return (T) getData().getMap0();
|
||||
case MAP1:
|
||||
return (T) getData().getMap1();
|
||||
case VECTOR0:
|
||||
return (T) getData().getVector0();
|
||||
case VECTOR1:
|
||||
return (T) getData().getVector1();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public PacketData getData() {
|
||||
return data.get();
|
||||
}
|
||||
|
||||
public void setData(PacketData d) {
|
||||
data.set(d);
|
||||
setID(data.get().getID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("id", String.valueOf(getID()));
|
||||
map.put("i0", String.valueOf(((Integer) getDataValue(PacketDataID.INT0)).intValue()));
|
||||
map.put("i1", String.valueOf(((Integer) getDataValue(PacketDataID.INT1)).intValue()));
|
||||
map.put("s0", getDataValue(PacketDataID.STRING0));
|
||||
map.put("s1", getDataValue(PacketDataID.STRING1));
|
||||
map.put("l0", String.valueOf(((Long) getDataValue(PacketDataID.LONG0)).longValue()));
|
||||
map.put("l1", String.valueOf(((Long) getDataValue(PacketDataID.LONG1)).longValue()));
|
||||
map.put("d0", String.valueOf(((Double) getDataValue(PacketDataID.DOUBLE0)).doubleValue()));
|
||||
map.put("d1", String.valueOf(((Double) getDataValue(PacketDataID.DOUBLE1)).doubleValue()));
|
||||
map.put("f0", String.valueOf(((Float) getDataValue(PacketDataID.FLOAT0)).floatValue()));
|
||||
map.put("f1", String.valueOf(((Float) getDataValue(PacketDataID.FLOAT1)).floatValue()));
|
||||
map.put("m0", Tools.mapToString(getDataValue(PacketDataID.MAP0)));
|
||||
map.put("m1", Tools.mapToString(getDataValue(PacketDataID.MAP1)));
|
||||
Map<String, String> v0map = new HashMap<>();
|
||||
v0map.put("x", String.valueOf(((Vector3d) getDataValue(PacketDataID.VECTOR0)).getX()));
|
||||
v0map.put("y", String.valueOf(((Vector3d) getDataValue(PacketDataID.VECTOR0)).getY()));
|
||||
v0map.put("z", String.valueOf(((Vector3d) getDataValue(PacketDataID.VECTOR0)).getZ()));
|
||||
map.put("v0", Tools.mapToString(v0map));
|
||||
Map<String, String> v1map = new HashMap<>();
|
||||
v1map.put("x", String.valueOf(((Vector3d) getDataValue(PacketDataID.VECTOR1)).getX()));
|
||||
v1map.put("y", String.valueOf(((Vector3d) getDataValue(PacketDataID.VECTOR1)).getY()));
|
||||
v1map.put("z", String.valueOf(((Vector3d) getDataValue(PacketDataID.VECTOR1)).getZ()));
|
||||
map.put("v1", Tools.mapToString(v1map));
|
||||
return Tools.mapToString(map);
|
||||
}
|
||||
|
||||
public void sendAsPieces(int bytesPerPiece, Connection c) throws IOException {
|
||||
String d = toString();
|
||||
c.send("[BEGIN(" + d.length() + ")[");
|
||||
|
||||
for (int i = 0; i < d.length(); i += bytesPerPiece) {
|
||||
StringBuilder piece = new StringBuilder();
|
||||
for (int j = 0; j < bytesPerPiece; j++) {
|
||||
try {
|
||||
piece.append(d.toCharArray()[i + j]);
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException ignore) {
|
||||
}
|
||||
}
|
||||
c.send(piece.toString());
|
||||
}
|
||||
|
||||
c.send("]END]");
|
||||
}
|
||||
}
|
82
src/main/java/de/tudbut/net/nethandler/PacketData.java
Normal file
82
src/main/java/de/tudbut/net/nethandler/PacketData.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
package de.tudbut.net.nethandler;
|
||||
|
||||
import de.tudbut.net.ws.Connection;
|
||||
import de.tudbut.type.Vector3d;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Deprecated
|
||||
public interface PacketData {
|
||||
int getID();
|
||||
|
||||
default String getString0() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default String getString1() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default int getInt0() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default int getInt1() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default long getLong0() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default long getLong1() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default Map<String, String> getMap0() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
default Map<String, String> getMap1() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
default double getDouble0() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default double getDouble1() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default float getFloat0() {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
default float getFloat1() {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
default Vector3d getVector0() {
|
||||
return new Vector3d(0, 0, 0);
|
||||
}
|
||||
|
||||
default Vector3d getVector1() {
|
||||
return new Vector3d(0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
default Packet toPacket() {
|
||||
return new Packet(this);
|
||||
}
|
||||
|
||||
default String asString() {
|
||||
return new Packet(this).toString();
|
||||
}
|
||||
|
||||
default void sendInPieces(int bytesPerPiece, Connection connection) throws IOException {
|
||||
new Packet(this).sendAsPieces(bytesPerPiece, connection);
|
||||
}
|
||||
}
|
6
src/main/java/de/tudbut/net/nethandler/PacketDataID.java
Normal file
6
src/main/java/de/tudbut/net/nethandler/PacketDataID.java
Normal file
|
@ -0,0 +1,6 @@
|
|||
package de.tudbut.net.nethandler;
|
||||
|
||||
@Deprecated
|
||||
public enum PacketDataID {
|
||||
ID, INT0, INT1, STRING0, STRING1, LONG0, LONG1, DOUBLE0, DOUBLE1, FLOAT0, FLOAT1, MAP0, MAP1, VECTOR0, VECTOR1
|
||||
}
|
40
src/main/java/de/tudbut/net/pbic2/PBIC2.java
Normal file
40
src/main/java/de/tudbut/net/pbic2/PBIC2.java
Normal file
|
@ -0,0 +1,40 @@
|
|||
package de.tudbut.net.pbic2;
|
||||
|
||||
import de.tudbut.io.TypedInputStream;
|
||||
import de.tudbut.io.TypedOutputStream;
|
||||
import de.tudbut.tools.ReflectUtil;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.Socket;
|
||||
|
||||
public interface PBIC2 {
|
||||
|
||||
String readMessage() throws IOException;
|
||||
|
||||
String writeMessage(String s) throws IOException;
|
||||
|
||||
String writeMessageWithResponse(String s) throws IOException;
|
||||
|
||||
Socket getSocket();
|
||||
TypedInputStream getInput();
|
||||
TypedOutputStream getOutput();
|
||||
|
||||
default boolean isSSL() {
|
||||
return getSocket() instanceof SSLSocket;
|
||||
}
|
||||
default Socket getRealSocket() {
|
||||
if(!isSSL())
|
||||
throw new IllegalStateException("getRealSocket() called despite socket not being SSL");
|
||||
try {
|
||||
Class<?> BaseSSLSocketImpl = Class.forName("sun.security.ssl.BaseSSLSocketImpl");
|
||||
Field self = BaseSSLSocketImpl.getDeclaredField("self");
|
||||
ReflectUtil.forceAccessible(self);
|
||||
return (Socket) self.get(getSocket());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("cannot call getRealSocket() on this JVM");
|
||||
}
|
||||
}
|
||||
}
|
4
src/main/java/de/tudbut/net/pbic2/PBIC2ADisconnect.java
Normal file
4
src/main/java/de/tudbut/net/pbic2/PBIC2ADisconnect.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
package de.tudbut.net.pbic2;
|
||||
|
||||
public class PBIC2ADisconnect extends Exception {
|
||||
}
|
98
src/main/java/de/tudbut/net/pbic2/PBIC2AEventHandler.java
Normal file
98
src/main/java/de/tudbut/net/pbic2/PBIC2AEventHandler.java
Normal file
|
@ -0,0 +1,98 @@
|
|||
package de.tudbut.net.pbic2;
|
||||
|
||||
import de.tudbut.obj.TLMap;
|
||||
import de.tudbut.type.Stoppable;
|
||||
import de.tudbut.io.TypedInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class PBIC2AEventHandler implements Stoppable {
|
||||
|
||||
private final ArrayList<PBIC2> array = new ArrayList<>();
|
||||
private final TLMap<PBIC2, Integer> map = new TLMap<>();
|
||||
private final TLMap<PBIC2, PBIC2AListener> listeners = new TLMap<>();
|
||||
private final TLMap<PBIC2, InputStream> inputStreams = new TLMap<>();
|
||||
|
||||
public void start(PBIC2 pbic2, PBIC2AListener listener) throws IOException {
|
||||
if(array.contains(pbic2))
|
||||
return;
|
||||
array.add(pbic2);
|
||||
listeners.set(pbic2, listener);
|
||||
inputStreams.set(pbic2, pbic2.getInput().getStream());
|
||||
}
|
||||
public void remove(PBIC2 pbic2) {
|
||||
if(!array.contains(pbic2))
|
||||
return;
|
||||
array.remove(pbic2);
|
||||
map.set(pbic2, null);
|
||||
listeners.set(pbic2, null);
|
||||
inputStreams.set(pbic2, null);
|
||||
}
|
||||
|
||||
public PBIC2AEventHandler() {
|
||||
new Thread(this::run).start();
|
||||
}
|
||||
|
||||
private void runOn(PBIC2 pbic2) throws IOException {
|
||||
Socket socket = pbic2.getSocket();
|
||||
InputStream stream = inputStreams.get(pbic2);
|
||||
TypedInputStream tis = pbic2.getInput();
|
||||
if (!socket.isConnected() || socket.isClosed()) {
|
||||
listeners.get(pbic2).onError(new PBIC2ADisconnect());
|
||||
remove(pbic2);
|
||||
}
|
||||
if(pbic2.isSSL()) {
|
||||
if (pbic2.getRealSocket().getInputStream().available() > 0) {
|
||||
listeners.get(pbic2).onMessage(tis.readString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (map.get(pbic2) == null) {
|
||||
if (stream.available() >= 4)
|
||||
map.set(pbic2, tis.readInt());
|
||||
}
|
||||
else if (stream.available() >= map.get(pbic2) * 2) {
|
||||
int i = map.get(pbic2);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int j = 0 ; j < i ; j++) {
|
||||
builder.append(tis.readChar());
|
||||
}
|
||||
listeners.get(pbic2).onMessage(builder.toString());
|
||||
map.set(pbic2, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void run() {
|
||||
while (!isStopped()) {
|
||||
ArrayList<PBIC2> array = this.array;
|
||||
try {
|
||||
for (int i = 0 ; i < array.size() ; i++) {
|
||||
try {
|
||||
runOn(array.get(i));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
listeners.get(array.get(i)).onError(e);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) { }
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
catch (InterruptedException ignored) { }
|
||||
}
|
||||
}
|
||||
|
||||
private int readWithTimeout(TypedInputStream stream) throws IOException {
|
||||
try {
|
||||
return stream.read();
|
||||
} catch (SocketTimeoutException e) {
|
||||
// There is no data available.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
10
src/main/java/de/tudbut/net/pbic2/PBIC2AListener.java
Normal file
10
src/main/java/de/tudbut/net/pbic2/PBIC2AListener.java
Normal file
|
@ -0,0 +1,10 @@
|
|||
package de.tudbut.net.pbic2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface PBIC2AListener {
|
||||
|
||||
void onMessage(String message) throws IOException;
|
||||
|
||||
void onError(Throwable throwable);
|
||||
}
|
96
src/main/java/de/tudbut/net/pbic2/PBIC2Client.java
Normal file
96
src/main/java/de/tudbut/net/pbic2/PBIC2Client.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
package de.tudbut.net.pbic2;
|
||||
|
||||
import de.tudbut.io.TypedInputStream;
|
||||
import de.tudbut.io.TypedOutputStream;
|
||||
import de.tudbut.net.http.HTTPHeader;
|
||||
import de.tudbut.net.http.HTTPRequest;
|
||||
import de.tudbut.net.http.HTTPResponse;
|
||||
import de.tudbut.net.http.HTTPResponseCode;
|
||||
import de.tudbut.net.http.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.Socket;
|
||||
|
||||
public final class PBIC2Client implements PBIC2 {
|
||||
|
||||
Socket socket;
|
||||
public final TypedInputStream in;
|
||||
public final TypedOutputStream out;
|
||||
|
||||
public PBIC2Client(HTTPRequest request) throws IOException {
|
||||
this(request, i -> i);
|
||||
}
|
||||
|
||||
public PBIC2Client(HTTPRequest request, PBIC2Passthrough passthrough) throws IOException {
|
||||
this(request, passthrough, passthrough);
|
||||
}
|
||||
|
||||
public PBIC2Client(HTTPRequest request, PBIC2Passthrough passthroughIn, PBIC2Passthrough passthroughOut) throws IOException {
|
||||
request.headers.removeIf(h -> h.key().equals("Connection"));
|
||||
request.headers.add(new HTTPHeader("Connection", "Upgrade"));
|
||||
request.headers.add(new HTTPHeader("Upgrade", "TudbuT/PBIC2"));
|
||||
socket = request.sendNoRead();
|
||||
socket.setSoLinger(false, 0);
|
||||
StringBuilder s = new StringBuilder();
|
||||
int res;
|
||||
while ((res = socket.getInputStream().read()) != 0) {
|
||||
s.append((char) res);
|
||||
if(res == 0x0a) {
|
||||
try {
|
||||
if (new HTTPResponse(s.toString()).parse().getStatusCodeAsEnum() != HTTPResponseCode.SwitchingProtocols) {
|
||||
throw new IOException("Invalid response.");
|
||||
}
|
||||
}
|
||||
catch (Exception ignored) { }
|
||||
}
|
||||
}
|
||||
socket.getOutputStream().write(0);
|
||||
InputStream adapter = socket.getInputStream();
|
||||
|
||||
in = new TypedInputStream(new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return passthroughIn.pass(adapter.read());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return adapter.available();
|
||||
}
|
||||
});
|
||||
out = new TypedOutputStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int i) throws IOException {
|
||||
socket.getOutputStream().write(passthroughOut.pass(i));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String readMessage() throws IOException {
|
||||
return in.readString();
|
||||
}
|
||||
|
||||
public synchronized String writeMessage(String s) throws IOException {
|
||||
return out.writeString(s);
|
||||
}
|
||||
|
||||
public synchronized String writeMessageWithResponse(String s) throws IOException {
|
||||
out.writeString(s);
|
||||
return in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedInputStream getInput() {
|
||||
return in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedOutputStream getOutput() {
|
||||
return out;
|
||||
}
|
||||
}
|
6
src/main/java/de/tudbut/net/pbic2/PBIC2Passthrough.java
Normal file
6
src/main/java/de/tudbut/net/pbic2/PBIC2Passthrough.java
Normal file
|
@ -0,0 +1,6 @@
|
|||
package de.tudbut.net.pbic2;
|
||||
|
||||
public interface PBIC2Passthrough {
|
||||
|
||||
int pass(int b);
|
||||
}
|
89
src/main/java/de/tudbut/net/pbic2/PBIC2Server.java
Normal file
89
src/main/java/de/tudbut/net/pbic2/PBIC2Server.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
package de.tudbut.net.pbic2;
|
||||
|
||||
import de.tudbut.io.TypedInputStream;
|
||||
import de.tudbut.io.TypedOutputStream;
|
||||
import de.tudbut.net.http.*;
|
||||
import de.tudbut.net.http.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class PBIC2Server implements PBIC2 {
|
||||
|
||||
Socket socket;
|
||||
public final TypedInputStream in;
|
||||
public final TypedOutputStream out;
|
||||
|
||||
public PBIC2Server(HTTPServerRequest request) throws IOException {
|
||||
this(request, i -> i);
|
||||
}
|
||||
|
||||
public PBIC2Server(HTTPServerRequest request, PBIC2Passthrough passthrough) throws IOException {
|
||||
this(request, passthrough, passthrough);
|
||||
}
|
||||
|
||||
public PBIC2Server(HTTPServerRequest request, PBIC2Passthrough passthroughIn, PBIC2Passthrough passthroughOut) throws IOException {
|
||||
ParsedHTTPValue p = request.parse();
|
||||
if(Arrays.stream(p.getHeaders()).noneMatch(h -> h.toString().equals("Connection: Upgrade"))) {
|
||||
throw new IOException("Invalid request.");
|
||||
}
|
||||
if(Arrays.stream(p.getHeaders()).noneMatch(h -> h.toString().equals("Upgrade: TudbuT/PBIC2"))) {
|
||||
throw new IOException("Invalid request.");
|
||||
}
|
||||
socket = request.socket;
|
||||
socket.getOutputStream().write(HTTPResponseFactory.create(HTTPResponseCode.SwitchingProtocols, "\u0000", HTTPContentType.ANY).value.getBytes(StandardCharsets.ISO_8859_1));
|
||||
while (socket.getInputStream().read() != 0);
|
||||
|
||||
InputStream adapter = socket.getInputStream();
|
||||
|
||||
in = new TypedInputStream(new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return passthroughIn.pass(adapter.read());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return adapter.available();
|
||||
}
|
||||
});
|
||||
out = new TypedOutputStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int i) throws IOException {
|
||||
socket.getOutputStream().write(passthroughOut.pass(i));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String readMessage() throws IOException {
|
||||
return in.readString();
|
||||
}
|
||||
|
||||
public synchronized String writeMessage(String s) throws IOException {
|
||||
return out.writeString(s);
|
||||
}
|
||||
|
||||
public synchronized String writeMessageWithResponse(String s) throws IOException {
|
||||
out.writeString(s);
|
||||
return in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedInputStream getInput() {
|
||||
return in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedOutputStream getOutput() {
|
||||
return out;
|
||||
}
|
||||
}
|
99
src/main/java/de/tudbut/net/smtp/SMTPDataOutput.java
Normal file
99
src/main/java/de/tudbut/net/smtp/SMTPDataOutput.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
package de.tudbut.net.smtp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
|
||||
public class SMTPDataOutput {
|
||||
private final SMTPSender parent;
|
||||
final PrintWriter write;
|
||||
|
||||
private boolean headersOpen = true;
|
||||
private boolean dataOpen = false;
|
||||
|
||||
SMTPDataOutput(SMTPSender sender, PrintWriter writer) {
|
||||
parent = sender;
|
||||
this.write = writer;
|
||||
}
|
||||
|
||||
private void ensureHeadersOpen() {
|
||||
if(!headersOpen)
|
||||
throw new HeadersClosedException();
|
||||
}
|
||||
private void ensureDataOpen() {
|
||||
if(!dataOpen)
|
||||
throw new DataClosedException();
|
||||
}
|
||||
|
||||
public void from(String s) {
|
||||
ensureHeadersOpen();
|
||||
write.println("From: " + s);
|
||||
}
|
||||
|
||||
public void to(String s) {
|
||||
ensureHeadersOpen();
|
||||
write.println("To: " + s);
|
||||
}
|
||||
|
||||
public void subject(String s) {
|
||||
ensureHeadersOpen();
|
||||
write.println("Subject: " + s);
|
||||
}
|
||||
|
||||
public void date(Date date) {
|
||||
ensureHeadersOpen();
|
||||
write.println("Date: " + DateTimeFormatter.ISO_DATE_TIME.format(date.toInstant()));
|
||||
}
|
||||
|
||||
public void header(String k, String v) {
|
||||
ensureHeadersOpen();
|
||||
write.println(k + ": " + v);
|
||||
}
|
||||
|
||||
public void closeHeaders() {
|
||||
ensureHeadersOpen();
|
||||
write.println();
|
||||
headersOpen = false;
|
||||
}
|
||||
|
||||
public PrintStream getDataOutputStream() {
|
||||
if(headersOpen)
|
||||
closeHeaders();
|
||||
|
||||
return new PrintStream(new OutputStream() {
|
||||
int last = 0x0A;
|
||||
|
||||
@Override
|
||||
public void write(int i) throws IOException {
|
||||
if(i != '.' && last == 0x0A)
|
||||
write.write(i);
|
||||
else
|
||||
write.write(" .");
|
||||
last = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
closeData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void closeData() throws IOException {
|
||||
ensureDataOpen();
|
||||
write.println(".");
|
||||
dataOpen = false;
|
||||
parent.expect250();
|
||||
}
|
||||
|
||||
public static class HeadersClosedException extends RuntimeException {
|
||||
|
||||
}
|
||||
public static class DataClosedException extends RuntimeException {
|
||||
|
||||
}
|
||||
}
|
103
src/main/java/de/tudbut/net/smtp/SMTPSender.java
Normal file
103
src/main/java/de/tudbut/net/smtp/SMTPSender.java
Normal file
|
@ -0,0 +1,103 @@
|
|||
package de.tudbut.net.smtp;
|
||||
|
||||
import de.tudbut.io.CLSPrintWriter;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.io.*;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SMTPSender {
|
||||
|
||||
InputStreamReader in;
|
||||
OutputStreamWriter out;
|
||||
PrintWriter write;
|
||||
BufferedReader read;
|
||||
|
||||
public BufferedReader getReader() {
|
||||
return read;
|
||||
}
|
||||
|
||||
|
||||
public SMTPSender(String host, int port, boolean ssl) throws IOException {
|
||||
this(socket(host, port, ssl));
|
||||
}
|
||||
|
||||
private static Socket socket(String host, int port, boolean ssl) throws IOException {
|
||||
Socket socket;
|
||||
if(ssl) {
|
||||
socket = SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
}
|
||||
else {
|
||||
socket = new Socket(host, port);
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
public SMTPSender(InputStream inputStream, OutputStream outputStream) {
|
||||
in = new InputStreamReader(inputStream, StandardCharsets.ISO_8859_1);
|
||||
out = new OutputStreamWriter(outputStream, StandardCharsets.ISO_8859_1);
|
||||
write = new CLSPrintWriter(out);
|
||||
((CLSPrintWriter)write).customLineSeparator = "\r\n";
|
||||
read = new BufferedReader(in);
|
||||
}
|
||||
|
||||
public SMTPSender(Socket socket) throws IOException {
|
||||
this(socket.getInputStream(), socket.getOutputStream());
|
||||
}
|
||||
|
||||
public void helo(String name) throws IOException {
|
||||
write.println("HELO " + name);
|
||||
expect220();
|
||||
}
|
||||
|
||||
public void from(String sender) throws IOException {
|
||||
write.println("MAIL FROM:" + sender);
|
||||
expect250();
|
||||
}
|
||||
|
||||
public void to(String recipient) throws IOException {
|
||||
write.println("RCPT TO:" + recipient);
|
||||
expect250();
|
||||
}
|
||||
|
||||
public SMTPDataOutput data() throws IOException {
|
||||
write.println("DATA");
|
||||
expect354();
|
||||
return new SMTPDataOutput(this, write);
|
||||
}
|
||||
|
||||
public void quit() throws IOException {
|
||||
write.println("QUIT");
|
||||
expect221();
|
||||
}
|
||||
|
||||
void expect220() throws IOException {
|
||||
String s = read.readLine();
|
||||
if(!s.replaceAll("-", " ").split(" ", 2)[0].equals("220")) {
|
||||
thrUnexpected(s);
|
||||
}
|
||||
}
|
||||
void expect250() throws IOException {
|
||||
String s = read.readLine();
|
||||
if(!s.replaceAll("-", " ").split(" ", 2)[0].equals("250")) {
|
||||
thrUnexpected(s);
|
||||
}
|
||||
}
|
||||
void expect354() throws IOException {
|
||||
String s = read.readLine();
|
||||
if(!s.replaceAll("-", " ").split(" ", 2)[0].equals("354")) {
|
||||
thrUnexpected(s);
|
||||
}
|
||||
}
|
||||
void expect221() throws IOException {
|
||||
String s = read.readLine();
|
||||
if(!s.replaceAll("-", " ").split(" ", 2)[0].equals("221")) {
|
||||
thrUnexpected(s);
|
||||
}
|
||||
}
|
||||
|
||||
private void thrUnexpected(String code) {
|
||||
throw new SMTPServerErrorException(code);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package de.tudbut.net.smtp;
|
||||
|
||||
public class SMTPServerErrorException extends RuntimeException {
|
||||
public SMTPServerErrorException(String code) {
|
||||
super("Code: " + code);
|
||||
}
|
||||
}
|
11
src/main/java/de/tudbut/net/websocket/Operation.java
Normal file
11
src/main/java/de/tudbut/net/websocket/Operation.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package de.tudbut.net.websocket;
|
||||
|
||||
class Operation {
|
||||
final int opcode;
|
||||
final byte[] data;
|
||||
|
||||
Operation(int opcode, byte[] data) {
|
||||
this.opcode = opcode;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
59
src/main/java/de/tudbut/net/websocket/WebSocket.java
Normal file
59
src/main/java/de/tudbut/net/websocket/WebSocket.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
package de.tudbut.net.websocket;
|
||||
|
||||
import de.tudbut.io.TypedInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class WebSocket {
|
||||
|
||||
InputStream in;
|
||||
TypedInputStream tin;
|
||||
|
||||
public Operation readFrame() throws IOException {
|
||||
int i;
|
||||
i = in.read();
|
||||
boolean fin = (i & 0b10000000) != 0;
|
||||
if((i & 0b01110000) != 0) {
|
||||
fail();
|
||||
}
|
||||
int opcode = i & 0xF;
|
||||
i = in.read();
|
||||
boolean mask = (i & 0b10000000) != 0;
|
||||
long payloadLen = i & 0b01111111;
|
||||
long tmpLen = payloadLen;
|
||||
if(payloadLen == 126) {
|
||||
tmpLen = tin.readChar();
|
||||
}
|
||||
if(payloadLen == 127) {
|
||||
tmpLen = tin.readLong();
|
||||
}
|
||||
payloadLen = tmpLen;
|
||||
int maskKey = 0;
|
||||
if(mask) {
|
||||
maskKey = tin.readInt();
|
||||
}
|
||||
int[] appData = new int[(int) Math.ceil(payloadLen / 4d)];
|
||||
for (int j = 0 ; j < appData.length ; j++) {
|
||||
i = tin.readInt();
|
||||
appData[j] = mask ? i ^ maskKey : i;
|
||||
}
|
||||
byte[] appDataBytes = new byte[(int) (appData.length - (4 - (payloadLen % 4)))];
|
||||
for (int j = 0 ; j < appData.length ;) {
|
||||
// TODO check the way this is executed by JVM! (should work tho)
|
||||
if(j < appDataBytes.length)
|
||||
appDataBytes[j] = (byte) (appData[j++] >> 8 * 3 & 0xff);
|
||||
if(j < appDataBytes.length)
|
||||
appDataBytes[j] = (byte) (appData[j++] >> 8 * 2 & 0xff);
|
||||
if(j < appDataBytes.length)
|
||||
appDataBytes[j] = (byte) (appData[j++] >> 8 * 1 & 0xff);
|
||||
if(j < appDataBytes.length)
|
||||
appDataBytes[j] = (byte) (appData[j++] >> 8 * 0 & 0xff);
|
||||
}
|
||||
return new Operation(opcode, appDataBytes);
|
||||
}
|
||||
|
||||
private void fail() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package de.tudbut.net.websocket;
|
||||
|
||||
public class WebSocketServer {
|
||||
}
|
9
src/main/java/de/tudbut/net/ws/Client.java
Normal file
9
src/main/java/de/tudbut/net/ws/Client.java
Normal file
|
@ -0,0 +1,9 @@
|
|||
package de.tudbut.net.ws;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Client extends Connection {
|
||||
public Client(String ip, int port) throws IOException {
|
||||
super(ip, port);
|
||||
}
|
||||
}
|
113
src/main/java/de/tudbut/net/ws/Connection.java
Normal file
113
src/main/java/de/tudbut/net/ws/Connection.java
Normal file
|
@ -0,0 +1,113 @@
|
|||
package de.tudbut.net.ws;
|
||||
|
||||
import de.tudbut.io.StreamReader;
|
||||
import de.tudbut.io.StreamWriter;
|
||||
import de.tudbut.timer.AsyncTask;
|
||||
import de.tudbut.type.IntArrayList;
|
||||
import de.tudbut.type.Nothing;
|
||||
import de.tudbut.type.Stoppable;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.Socket;
|
||||
|
||||
public class Connection implements Stoppable, Runnable {
|
||||
private final Socket theSocket;
|
||||
private final Runnable[] handlers = new Runnable[256];
|
||||
private int latestHandler = -1;
|
||||
|
||||
Connection(String ip, int port) throws IOException {
|
||||
theSocket = new Socket(ip, port);
|
||||
}
|
||||
|
||||
Connection(Socket socket) {
|
||||
theSocket = socket;
|
||||
}
|
||||
|
||||
public Socket getSocket() {
|
||||
return theSocket;
|
||||
}
|
||||
|
||||
public void send(String s) throws IOException {
|
||||
PrintWriter writer = new PrintWriter(new OutputStreamWriter(theSocket.getOutputStream()));
|
||||
writer.println(s);
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
public void send(int[] bin) throws IOException {
|
||||
StreamWriter writer = new StreamWriter(theSocket.getOutputStream());
|
||||
for (int i = 0; i < bin.length; i++) {
|
||||
if (bin[i] == 0x00) {
|
||||
writer.writeUnsignedByte(0x01);
|
||||
writer.writeUnsignedByte(0x01);
|
||||
continue;
|
||||
}
|
||||
if (bin[i] == 0x01) {
|
||||
writer.writeUnsignedByte(0x01);
|
||||
writer.writeUnsignedByte(0x02);
|
||||
}
|
||||
writer.writeUnsignedByte(bin[i]);
|
||||
}
|
||||
writer.writeUnsignedByte(0x00);
|
||||
}
|
||||
|
||||
public AsyncTask<Nothing> sendAsync(String s) {
|
||||
return new AsyncTask<>(() -> {
|
||||
send(s);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public AsyncTask<Nothing> sendAsync(int[] s) {
|
||||
return new AsyncTask<>(() -> {
|
||||
send(s);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public int[] receiveBin() throws IOException {
|
||||
while (!new InputStreamReader(getSocket().getInputStream()).ready());
|
||||
StreamReader reader = new StreamReader(theSocket.getInputStream());
|
||||
IntArrayList list = new IntArrayList();
|
||||
int i;
|
||||
while ((i = reader.readNextUnsignedByte()) != 0x00) {
|
||||
if(i == 0x01) {
|
||||
if(reader.readNextUnsignedByte() == 0x01)
|
||||
i = 0x00;
|
||||
if(reader.readNextUnsignedByte() == 0x02)
|
||||
i = 0x01;
|
||||
}
|
||||
|
||||
list.add(i);
|
||||
}
|
||||
return list.toIntArray();
|
||||
}
|
||||
|
||||
public String receive() throws IOException {
|
||||
while (!new InputStreamReader(getSocket().getInputStream()).ready());
|
||||
return new BufferedReader(new InputStreamReader(theSocket.getInputStream())).readLine();
|
||||
}
|
||||
|
||||
public void addReceiveHook(Runnable runnable) {
|
||||
latestHandler++;
|
||||
handlers[latestHandler] = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
if ((!isStopped() && !theSocket.isClosed() && theSocket.getInputStream().available() > 0)) {
|
||||
for (Runnable handler : handlers) {
|
||||
if (handler != null)
|
||||
new Thread(handler).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
8
src/main/java/de/tudbut/net/ws/ConnectionHandler.java
Normal file
8
src/main/java/de/tudbut/net/ws/ConnectionHandler.java
Normal file
|
@ -0,0 +1,8 @@
|
|||
package de.tudbut.net.ws;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface ConnectionHandler {
|
||||
void run(Connection connection) throws IOException;
|
||||
}
|
48
src/main/java/de/tudbut/net/ws/Server.java
Normal file
48
src/main/java/de/tudbut/net/ws/Server.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
package de.tudbut.net.ws;
|
||||
|
||||
import de.tudbut.tools.Tools;
|
||||
import de.tudbut.type.Stoppable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
public class Server extends Tools.ObjectMapping implements Runnable, Stoppable {
|
||||
private final ServerSocket serverSocket;
|
||||
private final ConnectionHandler[] handlers = new ConnectionHandler[256];
|
||||
private int latestHandler = -1;
|
||||
|
||||
public Server(int port) throws IOException {
|
||||
serverSocket = new ServerSocket(port);
|
||||
}
|
||||
|
||||
public void addHandler(ConnectionHandler connectionHandler) {
|
||||
latestHandler++;
|
||||
handlers[latestHandler] = connectionHandler;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
new Thread(() -> {
|
||||
while (!isStopped()) {
|
||||
try {
|
||||
Socket s = serverSocket.accept();
|
||||
s.setKeepAlive(true);
|
||||
for (ConnectionHandler handler : handlers) {
|
||||
if (handler != null)
|
||||
new Thread(() -> {
|
||||
try {
|
||||
handler.run(new Connection(s));
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue