Dsui.java
/*
* Copyright (c) 2021 Mārtiņš Avots (Martins Avots) and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0, or the MIT License,
* which is available at https://spdx.org/licenses/MIT.html.
*
* SPDX-License-Identifier: EPL-2.0 OR MIT
*/
package net.splitcells.dem.resource.communication.interaction;
import net.splitcells.dem.environment.config.EndTime;
import net.splitcells.dem.environment.config.ProgramName;
import net.splitcells.dem.data.set.SetWA;
import net.splitcells.dem.data.set.list.ListWA;
import net.splitcells.dem.environment.config.StartTime;
import net.splitcells.dem.lang.Xml;
import net.splitcells.dem.lang.dom.Domable;
import net.splitcells.dem.object.Discoverable;
import net.splitcells.dem.resource.communication.Flushable;
import net.splitcells.dem.resource.communication.Sender;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
import java.util.Optional;
import java.util.function.Predicate;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
import static net.splitcells.dem.Dem.environment;
import static net.splitcells.dem.lang.Xml.*;
import static net.splitcells.dem.lang.namespace.NameSpaces.DEN;
import static net.splitcells.dem.lang.namespace.NameSpaces.NATURAL;
import static net.splitcells.dem.lang.perspective.PerspectiveI.perspective;
import static net.splitcells.dem.object.Discoverable.NO_CONTEXT;
import static net.splitcells.dem.resource.communication.interaction.LogMessageI.logMessage;
import static net.splitcells.dem.utils.NotImplementedYet.notImplementedYet;
/**
* DSUI ^= Dom Stream and Stack based User Interface
* <p>TODO Create new implementation with custom rendering and without Java's
* XML code.</p>
* <p>TODO IDEA Support recursive stacking.</p>
* <p>TODO TOFIX Remove duplicate name space declaration.</p>
* <p>TODO Split log file into multiple, in order to avoid files that are too
* large for easy processing.</p>
* <p>TODO Implement a GUI framework based on event and message passing.</p>
*/
@Deprecated
public class Dsui implements Sui<LogMessage<Node>>, Flushable {
private static final String ENTRY_POINT = "ENTRY.POINT.237048021";
public static Dsui dsui(Sender<String> output, Predicate<LogMessage<Node>> messageFilter) {
Element execution = elementWithChildren(
elementWithChildren(rElement(DEN, "execution"), nameSpaceDecleration(NATURAL)),
elementWithChildren(DEN, "name", environment().config().configValue(ProgramName.class)),
elementWithChildren(NATURAL, "start-time", environment().config().configValue(StartTime.class).toString()),
textNode(ENTRY_POINT));
return dsui(output, execution, messageFilter);
}
private static Dsui dsui(Sender<String> output, Element root, Predicate<LogMessage<Node>> messageFilter) {
return new Dsui(output, root, messageFilter);
}
private final Sender<String> baseOutput;
private final Sender<String> contentOutput;
private final Element root;
private final Predicate<LogMessage<Node>> messageFilter;
private boolean isClosed = false;
public Dsui(Sender<String> output, Element root, Predicate<LogMessage<Node>> messageFilter) {
this.messageFilter = messageFilter;
baseOutput = requireNonNull(output);
this.root = requireNonNull(root);
{
// HACK
String tmp = Xml.toDocumentString(root);
if (!tmp.contains(Dsui.ENTRY_POINT)) {
throw new IllegalArgumentException(tmp);
}
// FIXME Remove last line if only whitespace.
baseOutput.append(tmp.split(Dsui.ENTRY_POINT)[0]);
}
contentOutput = Sender.extend(baseOutput, " ", "");
}
public <R extends ListWA<LogMessage<Node>>> R appendError(Throwable throwable) {
final var error = perspective("error");
error.withProperty("message", throwable.getMessage());
{
final var stackTraceValue = new StringWriter();
throwable.printStackTrace(new PrintWriter(stackTraceValue));
error.withProperty("stack-trace", stackTraceValue.toString());
}
return append(error.toDom(), Optional.empty(), LogLevel.CRITICAL);
}
@Deprecated
private <R extends ListWA<LogMessage<Node>>> R append(Node domable, LogLevel logLevel) {
return append(logMessage(domable, NO_CONTEXT, logLevel));
}
public <R extends ListWA<LogMessage<Node>>> R append(Domable domable, LogLevel logLevel) {
return append(logMessage(domable.toDom(), NO_CONTEXT, logLevel));
}
/**
* There is no real usage for the optionality of context.
*
* @param domable domable
* @param context context
* @param logLevel logLevel
* @param <R> type
* @return return
*/
@Deprecated
private <R extends ListWA<LogMessage<Node>>> R append(Node domable, Optional<Discoverable> context,
LogLevel logLevel) {
return append(logMessage(domable, context.orElse(NO_CONTEXT), logLevel));
}
public <R extends ListWA<LogMessage<Node>>> R append(Node domable, Discoverable context, LogLevel logLevel) {
return append(logMessage(domable, context, logLevel));
}
public <R extends ListWA<LogMessage<Node>>> R append(Domable domable, Discoverable context, LogLevel logLevel) {
return append(domable.toDom(), context, logLevel);
}
public <R extends ListWA<LogMessage<Node>>> R append(Domable domable, Optional<Discoverable> context,
LogLevel logLevel) {
return append(domable.toDom(), context, logLevel);
}
@SuppressWarnings("unchecked")
@Override
@Deprecated
public <R extends ListWA<LogMessage<Node>>> R append(LogMessage<Node> arg) {
if (messageFilter.test(arg)) {
print(arg.content());
}
return (R) this;
}
private void print(Node arg) {
asList(
Xml.toPrettyWithoutHeaderString(arg)
.split("\\R"))
.forEach(contentOutput::append);
}
@SuppressWarnings("unchecked")
@Deprecated
public <R extends ListWA<LogMessage<Node>>> R append(String text) {
// HACK
contentOutput.append(text);
return (R) this;
}
@Override
public void close() {
if (isClosed) {
throw new IllegalStateException();
}
final var endTime = environment().config().configValue(EndTime.class);
if (endTime.isPresent()) {
print(Xml.elementWithChildren(
Xml.rElement(NATURAL, "end-time"),
Xml.textNode(endTime.get().toString())));
print(Xml.elementWithChildren(
Xml.rElement(NATURAL, "runtime-in-seconds"),
Xml.textNode("" + Duration.between
(environment().config().configValue(StartTime.class)
, endTime.get())
.toSeconds())));
print(Xml.elementWithChildren(
Xml.rElement(NATURAL, "runtime-in-nanoseconds"),
Xml.textNode("" + Duration.between
(environment().config().configValue(StartTime.class)
, endTime.get())
.toNanos())));
}
String endingMessage = Xml.toPrettyString(root);
if (!endingMessage.contains(Dsui.ENTRY_POINT)) {
throw new IllegalArgumentException(endingMessage);
}
baseOutput.append(endingMessage.split(Dsui.ENTRY_POINT)[1]);
/**
* TODO FIX This does sometimes not work. See {@link Dem}.
*/
contentOutput.flush();
contentOutput.close();
isClosed = true;
}
@Override
public void flush() {
contentOutput.flush();
}
@Override
public <R extends SetWA<LogMessage<Node>>> R add(LogMessage<Node> value) {
throw notImplementedYet();
}
}