001/*
002 * Copyright (C) 2009-2017 the original author(s).
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fusesource.jansi;
017
018import java.io.IOException;
019import java.io.OutputStream;
020import java.util.ArrayList;
021import java.util.List;
022
023/**
024 * @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
025 */
026public class HtmlAnsiOutputStream extends AnsiOutputStream {
027
028    private boolean concealOn = false;
029
030    @Override
031    public void close() throws IOException {
032        closeAttributes();
033        super.close();
034    }
035
036    private static final String[] ANSI_COLOR_MAP = {"black", "red",
037            "green", "yellow", "blue", "magenta", "cyan", "white",};
038
039    private static final byte[] BYTES_QUOT = "&quot;".getBytes();
040    private static final byte[] BYTES_AMP = "&amp;".getBytes();
041    private static final byte[] BYTES_LT = "&lt;".getBytes();
042    private static final byte[] BYTES_GT = "&gt;".getBytes();
043
044    public HtmlAnsiOutputStream(OutputStream os) {
045        super(os);
046    }
047
048    private final List<String> closingAttributes = new ArrayList<String>();
049
050    private void write(String s) throws IOException {
051        super.out.write(s.getBytes());
052    }
053
054    private void writeAttribute(String s) throws IOException {
055        write("<" + s + ">");
056        closingAttributes.add(0, s.split(" ", 2)[0]);
057    }
058
059    private void closeAttributes() throws IOException {
060        for (String attr : closingAttributes) {
061            write("</" + attr + ">");
062        }
063        closingAttributes.clear();
064    }
065
066    public void write(int data) throws IOException {
067        switch (data) {
068            case 34: // "
069                out.write(BYTES_QUOT);
070                break;
071            case 38: // &
072                out.write(BYTES_AMP);
073                break;
074            case 60: // <
075                out.write(BYTES_LT);
076                break;
077            case 62: // >
078                out.write(BYTES_GT);
079                break;
080            default:
081                super.write(data);
082        }
083    }
084
085    public void writeLine(byte[] buf, int offset, int len) throws IOException {
086        write(buf, offset, len);
087        closeAttributes();
088    }
089
090    @Override
091    protected void processSetAttribute(int attribute) throws IOException {
092        switch (attribute) {
093            case ATTRIBUTE_CONCEAL_ON:
094                write("\u001B[8m");
095                concealOn = true;
096                break;
097            case ATTRIBUTE_INTENSITY_BOLD:
098                writeAttribute("b");
099                break;
100            case ATTRIBUTE_INTENSITY_NORMAL:
101                closeAttributes();
102                break;
103            case ATTRIBUTE_UNDERLINE:
104                writeAttribute("u");
105                break;
106            case ATTRIBUTE_UNDERLINE_OFF:
107                closeAttributes();
108                break;
109            case ATTRIBUTE_NEGATIVE_ON:
110                break;
111            case ATTRIBUTE_NEGATIVE_OFF:
112                break;
113            default:
114                break;
115        }
116    }
117
118    @Override
119    protected void processAttributeRest() throws IOException {
120        if (concealOn) {
121            write("\u001B[0m");
122            concealOn = false;
123        }
124        closeAttributes();
125    }
126
127    @Override
128    protected void processSetForegroundColor(int color, boolean bright) throws IOException {
129        writeAttribute("span style=\"color: " + ANSI_COLOR_MAP[color] + ";\"");
130    }
131
132    @Override
133    protected void processSetBackgroundColor(int color, boolean bright) throws IOException {
134        writeAttribute("span style=\"background-color: " + ANSI_COLOR_MAP[color] + ";\"");
135    }
136}