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