Initial commit

This commit is contained in:
459985746472861716 (TudbuT#2624) 2020-08-22 14:26:35 +02:00 committed by TudbuT
commit 37fe627477
Signed by: TudbuT
GPG key ID: B3CF345217F202D3
278 changed files with 20987 additions and 0 deletions

10
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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&amp;utm_medium=referral&amp;utm_content=TudbuT/tuddylib&amp;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
View 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

Binary file not shown.

View 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
View 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
View 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
View file

@ -0,0 +1,2 @@
rootProject.name = 'tuddylib'

View 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;
}
}
}

View 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 + ";";
}
}
}

View 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;
}
}

View file

@ -0,0 +1,11 @@
package de.tudbut.async;
/**
* @author TudbuT
* @since 03 Jun 2022
*/
public interface Callback<T> {
void call(T t);
}

View 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;
}
}

View 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);
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}

View 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);
}
}
}
}

View 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);
}
}

View 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();
}
}

View 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;
}
}

View 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();
}
}

View 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);
}
}
}
}

View 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
}
}

View 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();
}
}

View 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;
}
}

View 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);
}
}
}

View 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);
}
}

View 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());
}
}

View 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;
}
}

View 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();
}
}

View 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();
}
}
}
}
}

View 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))));
}
}

View 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;
}
}

View 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();
}
}

View 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;
}
}

View 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");
}
}

View 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;
});
}
}

View 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();
}
}

View 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;
}
}

View 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;
}
}

View 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,
;
}

View 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);
}
}

View 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;
}
}

View 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());
}
}

View 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());
}
}

View 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));
}
}

View 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;
}
}

View 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());
}
}

View 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;
}
}

View 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();
}
}

View 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;
}
}

View 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,
;
}

View 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;
}
};
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}
}

View 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;
}
};
}
}

View 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);
}
};
}
}

View 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();
}

View 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();
}

View 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;
}
}
}

View 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();
}

View 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;
}
}
}

View 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();
}

View 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();
}

View 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);
}
}

View 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,
;
}
}

View 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;
}
}

View 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();
}
}
}

View 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]");
}
}

View 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);
}
}

View 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
}

View 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");
}
}
}

View file

@ -0,0 +1,4 @@
package de.tudbut.net.pbic2;
public class PBIC2ADisconnect extends Exception {
}

View 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;
}
}
}

View 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);
}

View 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;
}
}

View file

@ -0,0 +1,6 @@
package de.tudbut.net.pbic2;
public interface PBIC2Passthrough {
int pass(int b);
}

View 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;
}
}

View 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 {
}
}

View 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);
}
}

View file

@ -0,0 +1,7 @@
package de.tudbut.net.smtp;
public class SMTPServerErrorException extends RuntimeException {
public SMTPServerErrorException(String code) {
super("Code: " + code);
}
}

View 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;
}
}

View 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() {
}
}

View file

@ -0,0 +1,4 @@
package de.tudbut.net.websocket;
public class WebSocketServer {
}

View 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);
}
}

View 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();
}
}

View file

@ -0,0 +1,8 @@
package de.tudbut.net.ws;
import java.io.IOException;
public interface ConnectionHandler {
void run(Connection connection) throws IOException;
}

View 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