Initial commit
This commit is contained in:
commit
7048bdc21e
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry exported="true" kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="module" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
|
@ -0,0 +1 @@
|
||||||
|
target/
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>irctokens</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
|
@ -0,0 +1,16 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=17
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
|
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
|
||||||
|
org.eclipse.jdt.core.compiler.release=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.source=17
|
|
@ -0,0 +1,4 @@
|
||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
version=1
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 David Schultz
|
||||||
|
|
||||||
|
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, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,12 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>me.zpld</groupId>
|
||||||
|
<artifactId>irctokens</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>irctokens</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
package me.zpld.irctokens;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
public static final String[] TAG_UNESCAPED = {"\\", " ", ";", "\r", "\n"};
|
||||||
|
public static final String[] TAG_ESCAPED = {"\\\\", "\\s", "\\:", "\\r", "\\n"};
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package me.zpld.irctokens;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Formatting {
|
||||||
|
private static String escapeTag(String tag) {
|
||||||
|
for (int i = 0; i < Constants.TAG_UNESCAPED.length; i++)
|
||||||
|
tag = tag.replaceAll(Constants.TAG_UNESCAPED[i], Constants.TAG_ESCAPED[i]);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(HashMap<String, String> tags, String source, String command, List<String> params) throws Exception {
|
||||||
|
List<String> outs = new ArrayList<>();
|
||||||
|
if (tags != null && tags.size() > 0) {
|
||||||
|
List<String> sTags = new ArrayList<>();
|
||||||
|
|
||||||
|
List<String> tagKeys = Arrays.asList((String[])tags.keySet().toArray());
|
||||||
|
Collections.sort(tagKeys);
|
||||||
|
for (String key : tagKeys) {
|
||||||
|
String value = tags.get(key);
|
||||||
|
if (value != null)
|
||||||
|
sTags.add(String.format("%s=%s", key, escapeTag(value)));
|
||||||
|
else
|
||||||
|
sTags.add(key);
|
||||||
|
}
|
||||||
|
outs.add("@" + String.join(";", sTags));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source != null)
|
||||||
|
outs.add(":"+source);
|
||||||
|
outs.add(command);
|
||||||
|
|
||||||
|
if (params.size() > 0) {
|
||||||
|
String last = params.get(params.size()-1);
|
||||||
|
for (String param : params.subList(0, params.size()-1)) {
|
||||||
|
if (param.indexOf(" ") >= 0)
|
||||||
|
throw new Exception("Non-last params cannot have spaces");
|
||||||
|
else if (param.startsWith(":"))
|
||||||
|
throw new Exception("Non-last params cannot start with colon");
|
||||||
|
}
|
||||||
|
outs.addAll(params.subList(0, params.size()-1));
|
||||||
|
if (last == null)
|
||||||
|
last = ":";
|
||||||
|
else if (last.indexOf(" ") >= 0 || last.startsWith(":"))
|
||||||
|
last = ":" + last;
|
||||||
|
outs.add(last);
|
||||||
|
}
|
||||||
|
return String.join(" ", outs);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
package me.zpld.irctokens;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Line {
|
||||||
|
private HashMap<String, String> tags;
|
||||||
|
private String source;
|
||||||
|
private String command;
|
||||||
|
private List<String> params;
|
||||||
|
|
||||||
|
public Line(HashMap<String, String> tags, String source, String command, List<String> params) {
|
||||||
|
this.tags = tags;
|
||||||
|
this.source = source;
|
||||||
|
this.command = command;
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Line(String command, String[] params) {
|
||||||
|
this(null, null, command, Arrays.asList(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format(
|
||||||
|
"Line(tags=%s, source=%s, command=%s, params=%s)",
|
||||||
|
tags, source, command, params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, String> getTags() {
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCommand() {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getParams() {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormatted() {
|
||||||
|
try {
|
||||||
|
return Formatting.format(tags, source, command, params);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String unescapeTag(String tag) {
|
||||||
|
String unescaped = "";
|
||||||
|
char[] escaped = tag.toCharArray();
|
||||||
|
int i = 0;
|
||||||
|
while (i < escaped.length) {
|
||||||
|
char current = escaped[i];
|
||||||
|
if (current == '\\') {
|
||||||
|
if (i < escaped.length - 1) {
|
||||||
|
char next = escaped[i + 1];
|
||||||
|
String duo = "" + current + next;
|
||||||
|
if (Arrays.asList(Constants.TAG_ESCAPED).contains(duo)) {
|
||||||
|
int index = Arrays.asList(Constants.TAG_ESCAPED).indexOf(duo);
|
||||||
|
unescaped += Constants.TAG_UNESCAPED[index];
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
unescaped += next;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unescaped += current;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unescaped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Line tokenise(String line) throws Exception {
|
||||||
|
HashMap<String, String> tags = new HashMap<>();
|
||||||
|
if (line.charAt(0) == '@') {
|
||||||
|
String[] tagParts = line.split(" ", 2);
|
||||||
|
String tags_s = tagParts[0];
|
||||||
|
line = tagParts.length > 1 ? tagParts[1] : "";
|
||||||
|
|
||||||
|
for (String part : tags_s.substring(1).split(";")) {
|
||||||
|
String[] kv = part.split("=", 2);
|
||||||
|
String key = kv[0];
|
||||||
|
String value = unescapeTag(kv.length > 1 ? kv[1] : "");
|
||||||
|
tags.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] lineParts = line.split(" :", 2);
|
||||||
|
line = lineParts[0];
|
||||||
|
String trailing = lineParts.length > 1 ? lineParts[1] : "";
|
||||||
|
List<String> params = new ArrayList<>(Arrays.asList(line.split(" ")));
|
||||||
|
|
||||||
|
String source = null;
|
||||||
|
if (params.size() > 0 && params.get(0).charAt(0) == ':') {
|
||||||
|
source = params.remove(0).substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.size() < 1)
|
||||||
|
throw new Exception("Cannot tokenise command-less line");
|
||||||
|
String command = params.remove(0);
|
||||||
|
|
||||||
|
if (trailing.length() > 0)
|
||||||
|
params.add(trailing);
|
||||||
|
|
||||||
|
return new Line(tags, source, command, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package me.zpld.irctokens;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class StatefulDecoder {
|
||||||
|
private Charset encoding;
|
||||||
|
private Charset fallback;
|
||||||
|
private byte[] buffer;
|
||||||
|
|
||||||
|
public StatefulDecoder(String encoding, String fallback) {
|
||||||
|
this.encoding = Charset.forName(encoding);
|
||||||
|
this.fallback = Charset.forName(fallback);
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatefulDecoder() {
|
||||||
|
this("UTF-8", "ISO-8859-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
this.buffer = new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] pending() {
|
||||||
|
return this.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Line> push(byte[] data) {
|
||||||
|
if (data == null || data.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.buffer = Arrays.copyOf(this.buffer, this.buffer.length + data.length);
|
||||||
|
System.arraycopy(data, 0, this.buffer, this.buffer.length - data.length, data.length);
|
||||||
|
List<Line> lines = new ArrayList<>();
|
||||||
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
while (end < this.buffer.length) {
|
||||||
|
if (this.buffer[end] == '\n') {
|
||||||
|
byte[] lineBytes = Arrays.copyOfRange(this.buffer, start, end);
|
||||||
|
Line line = tokenise(lineBytes, this.encoding, this.fallback);
|
||||||
|
lines.add(line);
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
this.buffer = Arrays.copyOfRange(this.buffer, start, end);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Line tokenise(byte[] lineBytes, Charset encoding, Charset fallback) {
|
||||||
|
String lineStr;
|
||||||
|
try {
|
||||||
|
lineStr = new String(lineBytes, encoding.name());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
lineStr = new String(lineBytes, fallback);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Line.tokenise(lineStr);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package me.zpld.irctokens;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class StatefulEncoder {
|
||||||
|
private String encoding;
|
||||||
|
private byte[] buffer;
|
||||||
|
private List<Line> bufferedLines;
|
||||||
|
|
||||||
|
public StatefulEncoder(String encoding) {
|
||||||
|
this.encoding = encoding;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatefulEncoder() {
|
||||||
|
this("UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
buffer = new byte[0];
|
||||||
|
bufferedLines = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] pending() {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(Line line) {
|
||||||
|
String formattedLine = line.getFormatted() + "\r\n";
|
||||||
|
byte[] encodedLine;
|
||||||
|
try {
|
||||||
|
encodedLine = formattedLine.getBytes(encoding);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffer = concat(buffer, encodedLine);
|
||||||
|
bufferedLines.add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Line> pop(int byte_count) {
|
||||||
|
int sent = 0;
|
||||||
|
int index = 0;
|
||||||
|
while (sent < byte_count && index < buffer.length) {
|
||||||
|
if (buffer[index] == '\n') {
|
||||||
|
sent++;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
byte[] sentBytes = new byte[index];
|
||||||
|
System.arraycopy(buffer, 0, sentBytes, 0, index);
|
||||||
|
buffer = Arrays.copyOfRange(buffer, index, buffer.length);
|
||||||
|
List<Line> sentLines = new ArrayList<>();
|
||||||
|
for (int i = 0; i < sent; i++) {
|
||||||
|
sentLines.add(bufferedLines.remove(0));
|
||||||
|
}
|
||||||
|
return sentLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] concat(byte[] a, byte[] b) {
|
||||||
|
byte[] result = new byte[a.length + b.length];
|
||||||
|
System.arraycopy(a, 0, result, 0, a.length);
|
||||||
|
System.arraycopy(b, 0, result, a.length, b.length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue