com.simiacryptus.util.io.HtmlNotebookOutput.java Source code

Java tutorial

Introduction

Here is the source code for com.simiacryptus.util.io.HtmlNotebookOutput.java

Source

/*
 * Copyright (c) 2018 by Andrew Charneski.
 *
 * The author licenses this file to you under the
 * Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance
 * with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.simiacryptus.util.io;

import com.simiacryptus.util.TableOutput;
import com.simiacryptus.util.Util;
import com.simiacryptus.util.lang.CodeUtil;
import com.simiacryptus.util.lang.TimedResult;
import com.simiacryptus.util.lang.UncheckedSupplier;
import com.simiacryptus.util.test.SysOutInterceptor;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Base64;
import java.util.UUID;

/**
 * The type Html notebook output.
 */
public class HtmlNotebookOutput implements NotebookOutput {
    private static final Logger log = LoggerFactory.getLogger(com.simiacryptus.util.io.HtmlNotebookOutput.class);

    /**
     * The constant DEFAULT_ROOT.
     */
    @javax.annotation.Nonnull
    public static String DEFAULT_ROOT = "https://github.com/SimiaCryptus/utilities/tree/master/";
    /**
     * The Working dir.
     */
    public final File workingDir;
    @javax.annotation.Nonnull
    private final PrintStream primaryOut;
    /**
     * The Source root.
     */
    public String sourceRoot = com.simiacryptus.util.io.HtmlNotebookOutput.DEFAULT_ROOT;
    /**
     * The Excerpt number.
     */
    int excerptNumber = 0;
    private int maxOutSize = 8 * 1024;

    /**
     * Instantiates a new Html notebook output.
     *
     * @param parentDirectory the parent directory
     * @param out             the out
     * @throws FileNotFoundException the file not found exception
     */
    public HtmlNotebookOutput(final File parentDirectory, @javax.annotation.Nonnull final OutputStream out)
            throws FileNotFoundException {
        primaryOut = new PrintStream(out);
        workingDir = parentDirectory;
        out("<html><head><style>\n" + "pre {\n" + "    background-color: lightyellow;\n"
                + "    margin-left: 20pt;\n" + "    font-family: monospace;\n" + "}\n" + "</style></head><body>");
    }

    /**
     * Create html notebook output.
     *
     * @param parentDirectory the parent directory
     * @return the html notebook output
     * @throws FileNotFoundException the file not found exception
     */
    public static com.simiacryptus.util.io.HtmlNotebookOutput create(final File parentDirectory)
            throws FileNotFoundException {
        @javax.annotation.Nonnull
        final FileOutputStream out = new FileOutputStream(new File(parentDirectory, "index.html"));
        return new com.simiacryptus.util.io.HtmlNotebookOutput(parentDirectory, out) {
            @Override
            public void close() throws IOException {
                out.close();
            }
        };
    }

    @Override
    public void close() throws IOException {
        out("</body></html>");
        if (null != primaryOut) {
            primaryOut.close();
        }
    }

    @javax.annotation.Nonnull
    @SuppressWarnings("unchecked")
    @Override
    public <T> T code(@javax.annotation.Nonnull final UncheckedSupplier<T> fn, final int maxLog,
            final int framesNo) {
        try {
            final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            final StackTraceElement callingFrame = stackTrace[framesNo];
            final String sourceCode = CodeUtil.getInnerText(callingFrame);
            @javax.annotation.Nonnull
            final SysOutInterceptor.LoggedResult<TimedResult<Object>> result = SysOutInterceptor.withOutput(() -> {
                long priorGcMs = ManagementFactory.getGarbageCollectorMXBeans().stream()
                        .mapToLong(x -> x.getCollectionTime()).sum();
                final long start = System.nanoTime();
                try {
                    @Nullable
                    Object result1 = null;
                    try {
                        result1 = fn.get();
                    } catch (@javax.annotation.Nonnull final RuntimeException e) {
                        throw e;
                    } catch (@javax.annotation.Nonnull final Exception e) {
                        throw new RuntimeException(e);
                    }
                    long gcTime = ManagementFactory.getGarbageCollectorMXBeans().stream()
                            .mapToLong(x -> x.getCollectionTime()).sum() - priorGcMs;
                    return new TimedResult<Object>(result1, System.nanoTime() - start, gcTime);
                } catch (@javax.annotation.Nonnull final Throwable e) {
                    long gcTime = ManagementFactory.getGarbageCollectorMXBeans().stream()
                            .mapToLong(x -> x.getCollectionTime()).sum() - priorGcMs;
                    return new TimedResult<Object>(e, System.nanoTime() - start, gcTime);
                }
            });
            try {
                @javax.annotation.Nonnull
                final URI resolved = URI.create(sourceRoot)
                        .resolve(Util.pathTo(CodeUtil.projectRoot, CodeUtil.findFile(callingFrame)));
                out("<p>Code from <a href='%s#L%s'>%s:%s</a> executed in %.2f seconds: <br/>", resolved,
                        callingFrame.getLineNumber(), callingFrame.getFileName(), callingFrame.getLineNumber(),
                        result.obj.seconds());
            } catch (@javax.annotation.Nonnull final Exception e) {
                out("<p>Code from %s:%s executed in %.2f seconds: <br/>", callingFrame.getFileName(),
                        callingFrame.getLineNumber(), result.obj.seconds());
            }
            out("<pre>");
            out(sourceCode);
            out("</pre>");

            if (!result.log.isEmpty()) {
                out("Logging: <br/>");
                out("<pre>");
                out(summarize(maxLog, result.log));
                out("</pre>");
            }
            out("");

            final Object eval = result.obj.result;
            if (null != eval) {
                out("Returns: <br/>");
                String str;
                boolean escape;
                if (eval instanceof Throwable) {
                    @javax.annotation.Nonnull
                    final ByteArrayOutputStream out = new ByteArrayOutputStream();
                    ((Throwable) eval).printStackTrace(new PrintStream(out));
                    str = new String(out.toByteArray(), "UTF-8");
                    escape = true;//
                } else if (eval instanceof Component) {
                    str = image(Util.toImage((Component) eval), "Result");
                    escape = false;
                } else if (eval instanceof BufferedImage) {
                    str = image((BufferedImage) eval, "Result");
                    escape = false;
                } else if (eval instanceof TableOutput) {
                    str = ((TableOutput) eval).toHtmlTable();
                    escape = false;
                } else {
                    str = eval.toString();
                    escape = true;
                }
                if (escape) {
                    out("<pre>" + summarize(maxLog, str) + "</pre>");
                } else {
                    out(summarize(maxLog, str));
                }
                if (escape) {
                }
                out("\n\n");
                if (eval instanceof Throwable) {
                    throw new RuntimeException((Throwable) result.obj.result);
                }
            }
            out("</p>");
            return (T) eval;
        } catch (@javax.annotation.Nonnull final IOException e) {
            throw new RuntimeException(e);
        }
    }

    @javax.annotation.Nonnull
    @Override
    public OutputStream file(@javax.annotation.Nonnull final String name) {
        try {
            return new FileOutputStream(new File(getResourceDir(), name));
        } catch (@javax.annotation.Nonnull final FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    @javax.annotation.Nonnull
    @Override
    public String file(final String data, final String caption) {
        return file(data, excerptNumber++ + ".txt", caption);
    }

    @javax.annotation.Nonnull
    @Override
    public String file(byte[] data, @javax.annotation.Nonnull String filename, String caption) {
        try {
            IOUtils.write(data, new FileOutputStream(new File(getResourceDir(), filename)));
        } catch (@javax.annotation.Nonnull final IOException e) {
            throw new RuntimeException(e);
        }
        return "<a href='etc/" + filename + "'>" + caption + "</a>";
    }

    @javax.annotation.Nonnull
    @Override
    public String file(final String data, @javax.annotation.Nonnull final String fileName, final String caption) {
        try {
            IOUtils.write(data, new FileOutputStream(new File(getResourceDir(), fileName)),
                    Charset.forName("UTF-8"));
        } catch (@javax.annotation.Nonnull final IOException e) {
            throw new RuntimeException(e);
        }
        return "<a href='etc/" + fileName + "'>" + caption + "</a>";
    }

    /**
     * Gets resource dir.
     *
     * @return the resource dir
     */
    @javax.annotation.Nonnull
    public File getResourceDir() {
        @javax.annotation.Nonnull
        final File etc = new File(workingDir, "etc");
        etc.mkdirs();
        return etc;
    }

    /**
     * Gets source root.
     *
     * @return the source root
     */
    public String getSourceRoot() {
        return sourceRoot;
    }

    /**
     * Sets source root.
     *
     * @param sourceRoot the source root
     * @return the source root
     */
    @javax.annotation.Nonnull
    public com.simiacryptus.util.io.HtmlNotebookOutput setSourceRoot(final String sourceRoot) {
        this.sourceRoot = sourceRoot;
        return this;
    }

    @Override
    public void h1(final String fmt, final Object... args) {
        out("<h1>" + fmt + "</h1>", args);
    }

    @Override
    public void h2(final String fmt, final Object... args) {
        out("<h2>" + fmt + "</h2>", args);
    }

    @Override
    public void h3(final String fmt, final Object... args) {
        out("<h3>" + fmt + "</h3>", args);
    }

    @javax.annotation.Nonnull
    @Override
    public String image(@Nullable final BufferedImage rawImage, final String caption) throws IOException {
        if (null == rawImage)
            return "";
        new ByteArrayOutputStream();
        @javax.annotation.Nonnull
        final String thisImage = UUID.randomUUID().toString().substring(0, 8);
        @javax.annotation.Nonnull
        final File file = new File(getResourceDir(), "img" + thisImage + ".png");
        @javax.annotation.Nonnull
        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        ImageIO.write(rawImage, "png", buffer);
        final String pngSrc = Base64.getEncoder().encodeToString(buffer.toByteArray());
        if (pngSrc.length() < 4 * 1024) {
            return "<img src='data:image/png;base64," + pngSrc + "' alt='" + caption + "'/>";
        } else {
            @Nullable
            final BufferedImage stdImage = Util.resize(rawImage);
            if (stdImage != rawImage) {
                ImageIO.write(rawImage, "png", new File(getResourceDir(), "raw" + thisImage + ".png"));
            }
            ImageIO.write(stdImage, "png", file);
            return "<img src='etc/" + file.getName() + "' alt='" + caption + "'/>";
        }
    }

    @Override
    public String link(@javax.annotation.Nonnull final File file, final String text) {
        @Nullable
        String path = null;
        try {
            path = workingDir.getCanonicalFile().toPath().relativize(file.getCanonicalFile().toPath()).normalize()
                    .toString().replaceAll("\\\\", "/");
        } catch (@javax.annotation.Nonnull final IOException e) {
            throw new RuntimeException(e);
        }
        return String.format("<a href=\"%s\">%s</a>", path, text);
    }

    @Override
    public void out(@javax.annotation.Nonnull final String fmt, @javax.annotation.Nonnull final Object... args) {
        @javax.annotation.Nonnull
        final String msg = 0 == args.length ? fmt : String.format(fmt, args);
        primaryOut.println(msg);
        primaryOut.flush();
        log.info(msg);
    }

    @Override
    public void p(final String fmt, final Object... args) {
        out("<p>" + fmt + "</p>", args);
    }

    @Nullable
    @Override
    public String getFrontMatterProperty(String key) {
        return null;
    }

    @javax.annotation.Nonnull
    @Override
    public String getName() {
        return "www";
    }

    /**
     * Summarize string.
     *
     * @param maxLog the max log
     * @param string the string
     * @return the string
     */
    @javax.annotation.Nonnull
    public String summarize(final int maxLog, @javax.annotation.Nonnull final String string) {
        if (string.length() > maxLog * 2) {
            @javax.annotation.Nonnull
            final String left = string.substring(0, maxLog);
            @javax.annotation.Nonnull
            final String right = string.substring(string.length() - maxLog);
            final String link = String.format(file(string, "\n...skipping %s bytes...\n"),
                    string.length() - 2 * maxLog);
            return left + link + right;
        } else {
            return string;
        }
    }

    public int getMaxOutSize() {
        return maxOutSize;
    }
}