/*
* ExperienceReader.java
*
* Copyright (C) 2008 Pei Wang
*
* This file is part of Open-NARS.
*
* Open-NARS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Open-NARS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Open-NARS. If not, see <http://www.gnu.org/licenses/>.
*/
package nars.io;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import nars.util.Events.Answer;
import nars.NAR;
import nars.entity.Concept;
import nars.entity.Sentence;
import nars.entity.Task;
import nars.entity.TruthValue;
import nars.io.Output.ECHO;
import nars.io.Output.ERR;
import nars.io.Output.EXE;
import nars.io.Output.IN;
import nars.io.Output.OUT;
import nars.language.Statement;
import nars.operator.Operator;
/**
* To read and write experience as Task streams
*/
public class TextOutput extends Output {
private final NAR nar;
private String prefix = "";
private LineOutput outExp2;
private PrintWriter outExp;
private boolean showErrors = true;
private boolean showStackTrace = false;
private boolean showStamp = true;
private boolean showInput = true;
private float minPriority = 0;
public interface LineOutput {
public void println(String s);
}
/**
* Default constructor; adds the reasoner to a NAR's outptu channels
*
* @param n
*/
public TextOutput(NAR n) {
super(n, true);
this.nar = n;
}
public TextOutput(NAR n, LineOutput outExp2) {
this(n);
this.outExp2 = outExp2;
}
public TextOutput(NAR n, PrintWriter outExp) {
this(n, outExp, 0.0f);
}
public TextOutput(NAR n, PrintWriter outExp, float minPriority) {
this(n);
this.outExp = outExp;
this.minPriority = minPriority;
}
public TextOutput(NAR n, PrintStream ps) {
this(n, new PrintWriter(ps));
}
public TextOutput(NAR n, PrintStream ps, float minPriority) {
this(n, ps);
this.minPriority = minPriority;
}
public TextOutput(NAR n, StringWriter s) {
this(n, new PrintWriter(s));
}
/**
* Open an output experience file
*/
public void openSaveFile(String path) {
try {
outExp = new PrintWriter(new FileWriter(path));
} catch (IOException ex) {
System.out.println("i/o error: " + ex.getMessage());
}
}
/**
* Close an output experience file
*/
public void closeSaveFile() {
outExp.close();
stop();
}
/**
* Process the next chunk of output data
*
* @param lines The text to be displayed
*/
@Override
public void event(final Class channel, final Object... oo) {
if (!showErrors && (channel == ERR.class))
return;
if (!showInput && (channel == IN.class))
return;
if ((outExp!=null) || (outExp2!=null)) {
Object o = oo[0];
final String s = process(channel, o);
if (s!=null) {
if (outExp != null) {
outExp.println(prefix + s);
outExp.flush();
}
if (outExp2 != null) {
outExp2.println(prefix + s);
}
}
}
}
final StringBuilder result = new StringBuilder(16 /* estimate */);
public String process(final Class c, final Object o) {
if (o instanceof Task) {
if (!allowTask((Task)o))
return null;
}
return getOutputString(c, o, true, showStamp, nar, result, minPriority);
}
/** may be overridden in subclass to filter certain tasks */
protected boolean allowTask(Task t) {
return true;
}
public TextOutput setErrors(boolean errors) {
this.showErrors = errors;
return this;
}
public TextOutput setShowInput(boolean showInput) {
this.showInput = showInput;
return this;
}
public TextOutput setErrorStackTrace(boolean b) {
this.showStackTrace = true;
return this;
}
public TextOutput setLinePrefix(String prefix) {
this.prefix = prefix;
return this;
}
public static String getOutputString(final Class channel, Object signal, final boolean showChannel, final boolean showStamp, final NAR nar, final StringBuilder buffer) {
return getOutputString(channel, signal, showChannel, showStamp, nar, buffer, 0);
}
/** generates a human-readable string from an output channel and signal */
public static String getOutputString(final Class channel, Object signal, final boolean showChannel, final boolean showStamp, final NAR nar, final StringBuilder buffer, float minPriority) {
buffer.setLength(0);
if (showChannel)
buffer.append(channel.getSimpleName()).append(": ");
if (channel == ERR.class) {
if (signal instanceof Throwable) {
Throwable e = (Throwable)signal;
buffer.append(e.toString());
/*if (showStackTrace)*/ {
//buffer.append(" ").append(Arrays.asList(e.getStackTrace()));
}
}
else {
buffer.append(signal.toString());
}
}
else if ((channel == OUT.class) || (channel == IN.class) || (channel == ECHO.class) || (channel == EXE.class) || (channel == Answer.class)
|| (channel == ANTICIPATE.class) || (channel == DISAPPOINT.class) || (channel == CONFIRM.class)) {
if(channel == CONFIRM.class) {
buffer.append(signal.toString());
}
if (signal instanceof Task) {
Task t = (Task)signal;
if (t.getPriority() < minPriority)
return null;
if((channel == ANTICIPATE.class) || (channel == DISAPPOINT.class)) {
buffer.append(t.sentence.toString(nar, showStamp));
}
else
if (channel == Answer.class) {
Task task = t; //server / NARRun
Sentence answer = task.getBestSolution();
if(answer!=null)
buffer.append(answer.toString(nar, showStamp));
else
buffer.append(t.sentence.toString(nar, showStamp));
}
else
buffer.append(t.sentence.toString(nar, showStamp));
/*
Task root = t.getRootTask();
if (root!=null)
buffer.append(" {{").append(root.sentence).append("}}");
*/
// }
// else if (signal instanceof Sentence) {
// Sentence s = (Sentence)signal;
// buffer.append(s.toString(nar, showStamp));
} else {
buffer.append(signal.toString());
}
}
else {
buffer.append(signal.toString());
}
return Texts.unescape(buffer).toString();
}
public static CharSequence getOutputString(Class channel, Object signal, boolean showChannel, boolean showStamp, NAR nar) {
CharSequence s = getOutputString(channel, signal, showStamp, nar);
if (showChannel) {
String channelName = channel.getSimpleName();
StringBuilder r = new StringBuilder(s.length() + 2 + channelName.length());
return r.append(channel.getSimpleName()).append(": ").append(s);
}
else {
return s;
}
}
public static CharSequence getOutputString(Class channel, Object signal, final boolean showStamp, final NAR nar) {
return getOutputString(channel, signal, showStamp, nar, new StringBuilder());
}
/** generates a human-readable string from an output channel and signal */
public static CharSequence getOutputString(Class channel, Object signal, final boolean showStamp, final NAR nar, final StringBuilder buffer) {
buffer.setLength(0);
if (signal instanceof Exception) {
Exception e = (Exception)signal;
buffer.append(e.getClass().getSimpleName()).append(": ").append(e.getMessage());
/*if (showStackTrace)*/
{
buffer.append(" ").append(Arrays.asList(e.getStackTrace()));
}
}
else if (signal instanceof Task) {
Task t = (Task)signal;
Sentence s = t.sentence;
buffer.append(s.toString(nar, showStamp));
}
else if (signal instanceof Sentence) {
Sentence s = (Sentence)signal;
buffer.append(s.toString(nar, showStamp));
}
else if (signal instanceof Object[]) {
if (channel == Answer.class) {
Object[] o = (Object[])signal;
Task task = (Task)o[0];
Sentence belief = (Sentence)o[1];
Sentence question = task.sentence;
Sentence answer = belief;
buffer.append(answer.toString(nar, showStamp));
}
else
buffer.append( Arrays.toString((Object[])signal) );
}
else {
buffer.append(signal.toString());
}
return Texts.unescape(buffer);
}
public void stop() {
setActive(false);
}
/** generates a human-readable string from an output channel and signal */
public static String getOutputHTML(final Class channel, Object signal, final boolean showChannel, final boolean showStamp, final NAR nar) {
final StringBuilder buffer = new StringBuilder();
if (channel == OUT.class) {
buffer.append("<div style='clear: both; float:left'>OUT:</div>");
if (signal instanceof Task) {
Task t = (Task)signal;
Sentence s = t.getBestSolution();
if (s == null)
s = t.sentence;
if (s.truth!=null) {
buffer.append(getTruthHTML(s.truth));
}
buffer.append("<div style='float:left'><pre>").append(escapeHTML(s.toString(nar, showStamp))).append("</pre></div>");
buffer.append("<br/>");
/*
Task root = t.getRootTask();
if (root!=null)
buffer.append(" {{").append(root.sentence).append("}}");
*/
}
else if (signal instanceof Sentence) {
Sentence s = (Sentence)signal;
buffer.append(s.toString(nar, showStamp));
} else {
buffer.append(signal.toString());
}
return Texts.unescape(buffer).toString();
}
if (showChannel)
buffer.append(channel.getSimpleName()).append(": ");
if (channel == ERR.class) {
if (signal instanceof Exception) {
Exception e = (Exception)signal;
buffer.append(e.toString());
/*if (showStackTrace)*/
/*for (int i = 0; i < )
buffer.append(" ").append(Arrays.asList(e.getStackTrace() ));
}*/
}
else {
buffer.append(signal.toString());
}
}
else if ((channel == IN.class) || (channel == ECHO.class)) {
buffer.append(signal.toString());
}
else if (channel == EXE.class) {
if (signal instanceof Statement)
buffer.append(Operator.operationExecutionString((Statement)signal));
else {
buffer.append(signal.toString());
}
}
else {
buffer.append(signal.toString());
}
return "<div style='clear: both'>" + escapeHTML(Texts.unescape(buffer).toString()) + "</div>";
}
protected static StringBuilder getTruthHTML(TruthValue truth) {
StringBuilder b = new StringBuilder();
//http://css-tricks.com/html5-meter-element/
//b.append("<meter value='" + truth.getFrequency() + "' min='0' max='1.0'></meter>");
//b.append("<meter value='" + truth.getConfidence()+ "' min='0' max='1.0' style='background: none'></meter>");
b.append(freqColor(truth.getFrequency(), "4em"));
b.append(meter(truth.getConfidence(), "4em", "#00F"));
return b;
}
public static String freqColor(float value, String width) {
String pct = ((int)(Math.round(value * 100.0))) + "%";
final String background = "rgba(255,255,255,0.15)";
String foreground = value < 0.5 ?
"rgba(" + (int)(255*(0.5 - value)*2) + ",0,0," + (0.5 - value)*2 + ")" :
"rgba(0," + (int)(255*(value - 0.5)*2) + ",0," + (value-0.5)*2 + ")";
return "<div style='float:left;width: " + width + ";padding:2px;'>" +
"<div style='width:100%;background-color:" + foreground + ";text-align:center;'>" +
"<span>" + pct + "</span></div></div>";
}
public static String meter(float value, String width, String foreground) {
String pct = ((int)(Math.round(value * 100.0))) + "%";
final String background = "rgba(255,255,255,0.15)";
return "<div style='float:left;width: " + width + ";padding:2px;background:" + background + ";'>" +
"<div style='width:" + pct + ";background:" + foreground + ";text-align:center;'>" +
"<span>" + pct + "</span></div></div>";
}
/**
* @author http://www.rgagnon.com/javadetails/java-0306.html
*/
public static String escapeHTML(CharSequence string) {
StringBuilder sb = new StringBuilder(string.length());
// true if last char was blank
boolean lastWasBlankChar = false;
int len = string.length();
char c;
for (int i = 0; i < len; i++) {
c = string.charAt(i);
if (c == ' ') {
// blank gets extra work,
// this solves the problem you get if you replace all
// blanks with , if you do that you loss
// word breaking
if (lastWasBlankChar) {
lastWasBlankChar = false;
sb.append(" ");
} else {
lastWasBlankChar = true;
sb.append(' ');
}
} else {
lastWasBlankChar = false;
//
// HTML Special Chars
if (c == '"') {
sb.append(""");
}
else if (c == '\'') {
sb.append("'");
} else if (c == '&') {
sb.append("&");
} else if (c == '<') {
sb.append("<");
} else if (c == '>') {
sb.append(">");
} else if (c == '\n') { // Handle Newline
//sb.append("<br/>");
} else {
int ci = 0xffff & c;
if (ci < 160) // nothing special only 7 Bit
{
sb.append(c);
} else {
// Not 7 Bit use the unicode system
sb.append("");
sb.append(Integer.toString(ci));
sb.append(';');
}
}
}
}
return sb.toString();
}
public static CharSequence summarize( Iterable<? extends Concept> concepts) {
StringBuilder s = new StringBuilder();
for (Concept c : concepts) {
s.append(c.toString() + "\n");
}
return s;
}
}