This commit is contained in:
crschnick 2024-03-07 22:41:29 +00:00
parent 965dfe04e8
commit bb4e844da9
9 changed files with 3 additions and 265 deletions

View file

@ -1,7 +0,0 @@
Copyright 2022 Christopher Schnick
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.

View file

@ -10,7 +10,7 @@ of different operations.
### Inner Workings
- The underlying inter-process communication is realized through
TCP sockets on port `21721` on Windows and `21722` on Linux.
TCP sockets on port `21721` on Windows and `21723` on Linux/macOS.
- The data structures and exchange protocols are specified in the
[io.xpipe.beacon.exchange package](src/main/java/io/xpipe/beacon/exchange).
@ -59,6 +59,3 @@ By passing the property `io.xpipe.beacon.launchDebugDaemon=true`, the daemon is
i.e. will log more information and enable a few other options.
By passing the property `io.xpipe.beacon.attachDebuggerToDaemon=true`, it is possible to launch a daemon
in a mode where it is waiting to attach to a debugger first prior to starting up.

View file

@ -7,9 +7,6 @@ plugins {
apply from: "$rootDir/gradle/gradle_scripts/java.gradle"
apply from: "$rootDir/gradle/gradle_scripts/lombok.gradle"
dependencies {
}
version = rootProject.versionString
group = 'io.xpipe'
archivesBaseName = 'xpipe-beacon'

View file

@ -1,7 +0,0 @@
Copyright 2022 Christopher Schnick
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.

View file

@ -6,20 +6,12 @@
The XPipe core module contains all the shared core classes used by the API, beacon, and daemon implementation.
It contains the following packages:
- [charsetter](src/main/java/io/xpipe/core/charsetter): Classes for handling all things charset
related such as detection and handling of data streams with BOMs.
- [data](src/main/java/io/xpipe/core/data): Contains all definitions of the
internal XPipe data model and all the IO functionality to read and write these data structures.
For more information, see [XPipe data model](https://xpipe-io.readthedocs.io/en/latest/dev/model.html)
- [dialog](src/main/java/io/xpipe/core/dialog): In API to create server/daemon side CLI dialogs.
These are used by extensions for data source and data store configuration from the command line.
- [source](src/main/java/io/xpipe/core/source): The basic data source classes that are used by every data source implementation.
- [store](src/main/java/io/xpipe/core/store): The basic data store classes that are used by every data store implementation.
- [process](src/main/java/io/xpipe/core/process): Base classes for the shell process handling implementation.
- [util](src/main/java/io/xpipe/core/source): A few utility classes for serialization and more.
Every class is expected to be potentially used in the context of files and message exchanges.

View file

@ -1,6 +0,0 @@
package io.xpipe.core.charsetter;
public interface Charsettable {
StreamCharset getCharset();
}

View file

@ -1,173 +0,0 @@
package io.xpipe.core.charsetter;
import io.xpipe.core.store.StreamDataStore;
import io.xpipe.core.util.FailableConsumer;
import io.xpipe.core.util.FailableSupplier;
import lombok.Value;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public abstract class Charsetter {
private static final int MAX_BYTES = 8192;
public static Charsetter INSTANCE;
private static CharsetterUniverse universe;
protected Charsetter() {}
protected static void checkInit() {
if (universe == null) {
throw new IllegalStateException("Charsetter not initialized");
}
}
public static void init(CharsetterContext ctx) {
universe = CharsetterUniverse.create(ctx);
}
public static Charsetter get() {
return INSTANCE;
}
private static int count(byte[] outerArray, byte[] smallerArray) {
int count = 0;
for (int i = 0; i < outerArray.length - smallerArray.length + 1; ++i) {
boolean found = true;
for (int j = 0; j < smallerArray.length; ++j) {
if (outerArray[i + j] != smallerArray[j]) {
found = false;
break;
}
}
if (found) {
count++;
}
}
return count;
}
public BufferedReader reader(StreamDataStore store, StreamCharset charset) throws Exception {
return reader(store.openBufferedInput(), charset);
}
public OutputStreamWriter writer(StreamDataStore store, StreamCharset charset) throws Exception {
return new OutputStreamWriter(store.openOutput(), charset.getCharset());
}
public BufferedReader reader(InputStream stream, StreamCharset charset) throws Exception {
if (charset.hasByteOrderMark()) {
var bom = stream.readNBytes(charset.getByteOrderMark().length);
if (bom.length != 0 && !Arrays.equals(bom, charset.getByteOrderMark())) {
throw new IllegalStateException("Charset does not match: " + charset);
}
}
return new BufferedReader(new InputStreamReader(stream, charset.getCharset()));
}
public abstract Result read(FailableSupplier<InputStream> in, FailableConsumer<InputStreamReader, Exception> con);
public Result detect(StreamDataStore store) throws Exception {
Result result = new Result(null, null);
if (store.canOpen()) {
try (InputStream inputStream = store.openBufferedInput()) {
StreamCharset detected = null;
for (var charset : StreamCharset.COMMON) {
if (charset.hasByteOrderMark()) {
inputStream.mark(charset.getByteOrderMark().length);
var bom = inputStream.readNBytes(charset.getByteOrderMark().length);
inputStream.reset();
if (Arrays.equals(bom, charset.getByteOrderMark())) {
detected = charset;
break;
}
}
}
var bytes = inputStream.readNBytes(MAX_BYTES);
if (detected == null) {
detected = StreamCharset.get(inferCharset(bytes), false);
}
var nl = inferNewLine(bytes);
result = new Result(detected, nl);
}
}
// if (store instanceof FileStore fileStore && fileStore.getFileSystem() instanceof ShellStore m) {
// if (result.getNewLine() == null) {
// result = new Result(
// result.getCharset(),
// m.getShellType() != null ? m.getShellType().getNewLine() : null);
// }
// }
if (result.getCharset() == null) {
result = new Result(StreamCharset.UTF8, result.getNewLine());
}
if (result.getNewLine() == null) {
result = new Result(result.getCharset(), NewLine.platform());
}
return result;
}
public NewLine inferNewLine(byte[] content) {
Map<NewLine, Integer> count = new HashMap<>();
for (var nl : NewLine.values()) {
var nlBytes = nl.getNewLineString().getBytes(StandardCharsets.UTF_8);
count.put(nl, count(content, nlBytes));
}
if (count.values().stream().allMatch(v -> v == 0)) {
return null;
}
return count.entrySet().stream()
.min(Comparator.comparingInt(Map.Entry::getValue))
.orElseThrow()
.getKey();
}
public Charset inferCharset(byte[] content) {
checkInit();
for (Charset c : universe.getCharsets()) {
CharsetDecoder decoder = c.newDecoder();
decoder.onMalformedInput(CodingErrorAction.REPORT);
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
ByteBuffer byteBuf = ByteBuffer.wrap(content);
CharBuffer charBuf = CharBuffer.allocate(byteBuf.capacity() * 2);
CoderResult coderResult = decoder.decode(byteBuf, charBuf, false);
if (coderResult != null) {
if (coderResult.isError()) {
continue;
}
}
return c;
}
return StandardCharsets.UTF_8;
}
@Value
public static class Result {
StreamCharset charset;
NewLine newLine;
}
}

View file

@ -1,23 +0,0 @@
package io.xpipe.core.charsetter;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Locale;
@Value
@AllArgsConstructor
public class CharsetterContext {
String systemCharsetName;
Locale systemLocale;
Locale appLocale;
List<String> observedCharsets;
public static CharsetterContext empty() {
return new CharsetterContext(
Charset.defaultCharset().name(), Locale.getDefault(), Locale.getDefault(), List.of());
}
}

View file

@ -1,32 +0,0 @@
package io.xpipe.core.charsetter;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@Value
@AllArgsConstructor
public class CharsetterUniverse {
List<Charset> charsets;
public static CharsetterUniverse create(CharsetterContext ctx) {
List<Charset> cs = new ArrayList<>();
cs.add(StandardCharsets.UTF_8);
var system = Charset.forName(ctx.getSystemCharsetName());
cs.add(system);
// TODO: Locales
var observed = ctx.getObservedCharsets().stream().map(Charset::forName).toList();
cs.addAll(observed);
return new CharsetterUniverse(cs);
}
}