All Downloads are FREE. Search and download functionalities are using the official Maven repository.

us.bpsm.edn.printer.Printers Maven / Gradle / Ivy

Go to download

edn-java is an parser and printer for 'edn' written in Java, for Java and requiring no external dependencies.

There is a newer version: 0.7.1
Show newest version
// (c) 2012 B Smith-Mannschott -- Distributed under the Eclipse Public License
package us.bpsm.edn.printer;

import java.io.IOException;
import java.io.Writer;
import java.io.Closeable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.Set;
import java.util.UUID;

import us.bpsm.edn.EdnException;
import us.bpsm.edn.EdnIOException;
import us.bpsm.edn.Keyword;
import us.bpsm.edn.Symbol;
import us.bpsm.edn.Tag;
import us.bpsm.edn.TaggedValue;
import us.bpsm.edn.parser.InstantUtils;
import us.bpsm.edn.parser.Parser;
import us.bpsm.edn.protocols.Protocol;
import us.bpsm.edn.protocols.Protocols;
import us.bpsm.edn.util.CharClassify;


/**
 * Factory for creating {@link Printer}s and related Objects.
 */
public class Printers {

    private Printers() {
        throw new UnsupportedOperationException();
    }

    /**
     * Return a new Printer with the default printing
     * protocol. Everything the printer prints will be appended to
     * {@code out}. {@link Printer#close()} will close {@code
     * out}, provided {@code out} implements {@link Closeable}.
     *
     * @param out to which values will be printed. Never null.
     *
     * @return a Printer with default configuration, never null.
     */
    public static Printer newPrinter(final Appendable out) {
        return newPrinter(defaultPrinterProtocol(), out);
    }

    /**
     * Print {@code ednValue} to a new String using the default
     * printing protocol.
     *
     * @param ednValue the value to be returned as a String in edn syntax.
     *
     * @return A string in edn syntax. Not null, not empty.
     */
    public static String printString(Object ednValue) {
        return printString(defaultPrinterProtocol(), ednValue);
    }

    /**
     * Print {@code ednValue} to a new String using the printing
     * protocol given as {@code fns}.
     *
     * @param fns a Protocol which knows how to print all the classes
     *        of objects that we'll be asking our Printer to print.
     *        Never null. Never null.
     *
     * @param ednValue the value to be returned as a String in edn syntax.
     *
     * @return A string in edn syntax. Not null, not empty.
     */
    public static String printString(final Protocol> fns,
                                     Object ednValue) {
        StringBuilder sb = new StringBuilder();
        newPrinter(fns, sb).printValue(ednValue);
        return sb.toString();
    }

    /**
     * Return a new Printer with the printing protocol given as {@code
     * fns}. Everything the printer prints will be appended to {@code
     * writer}. {@link Printer#close()} will close {@code out}, if
     * {@code out} implements {@link Closeable}.
     *
     * @param fns a Protocol which knows how to print all the classes
     *        of objects that we'll be asking our Printer to print.
     *        Never null. Never null.
     * @param out to which values will be printed. Never null.
     *
     * @return a Printer, never null.
     */
    public static Printer newPrinter(final Protocol> fns,
                                     final Appendable out) {
        return new Printer() {
            int softspace = 0;

            public void close() {
                if (out instanceof Closeable) {
                    try {
                        ((Closeable)out).close();
                    } catch (IOException e) {
                        throw new EdnIOException(e);
                    }
                }
            }

            public Printer append(CharSequence csq) {
                try {
                    if (softspace > 1 && csq.length() > 0 &&
                            !CharClassify.isWhitespace(csq.charAt(0))) {
                        out.append(' ');
                    }
                    softspace = 0;
                    out.append(csq);
                    return this;
                } catch (IOException e) {
                    throw new EdnIOException(e);
                }
            }

            public Printer append(char c) {
                try {
                    if (softspace > 1 && !CharClassify.isWhitespace(c)) {
                        out.append(' ');
                    }
                    softspace = 0;
                    out.append(c);
                    return this;
                } catch (IOException e) {
                    throw new EdnIOException(e);
                }
            }

            public Printer printValue(Object ednValue) {
                @SuppressWarnings("unchecked")
                Printer.Fn printFn = (Printer.Fn)
                    fns.lookup(getClassOrNull(ednValue));
                if (printFn == null) {
                    throw new EdnException(String.format(
                            "Don't know how to write '%s' of type '%s'",
                            ednValue, getClassOrNull(ednValue)));
                }
                printFn.eval(ednValue, this);
                return this;
            }

            public Printer softspace() {
                softspace += 1;
                return this;
            }

        };
    }

    static Class getClassOrNull(Object o) {
        return o == null ? null : o.getClass();
    }

    /**
     * Returns a {@link us.bpsm.edn.protocols.Protocol.Builder}
     * preconfigured knowing how to print classes known to end-java:
     *
     * 
    *
  • {@link BigDecimal}
  • *
  • {@link BigInteger}
  • *
  • {@link Boolean}
  • *
  • {@link Byte} (as an integer)
  • *
  • {@link CharSequence} (as a string literal)
  • *
  • {@link Character} (as a character literal)
  • *
  • {@link Date} (as {@code #inst})
  • *
  • {@link Double}
  • *
  • {@link Float}
  • *
  • {@link GregorianCalendar} (as {@code #inst})
  • *
  • {@link Integer}
  • *
  • {@link Keyword}
  • *
  • {@link List}
  • *
  • {@link Long}
  • *
  • {@link Map}
  • *
  • {@link Set}
  • *
  • {@link Short} (as an integer)
  • *
  • {@link Symbol}
  • *
  • {@link Tag}
  • *
  • {@link TaggedValue}
  • *
  • {@link Timestamp} (as {@code #inst})
  • *
  • {@link UUID} (as {@code #uuid})
  • *
*/ public static Protocol.Builder> defaultProtocolBuilder() { Protocol.Builder> protocolBuilder = Protocols.>builder("print") .put(null, writeNullFn()) .put(BigDecimal.class, writeBigDecimalFn()) .put(BigInteger.class, writeBigIntegerFn()) .put(Boolean.class, writeBooleanFn()) .put(Byte.class, writeLongValueFn()) .put(CharSequence.class, writeCharSequenceFn()) .put(Character.class, writeCharacterFn()) .put(Date.class, writeDateFn()) .put(Double.class, writeDoubleValueFn()) .put(Float.class, writeDoubleValueFn()) .put(GregorianCalendar.class, writeCalendarFn()) .put(Integer.class, writeLongValueFn()) .put(Keyword.class, writeKeywordFn()) .put(List.class, writeListFn()) .put(Long.class, writeLongValueFn()) .put(Map.class, writeMapFn()) .put(Set.class, writeSetFn()) .put(Short.class, writeLongValueFn()) .put(Symbol.class, writeSymbolFn()) .put(Tag.class, writeTagFn()) .put(TaggedValue.class, writeTaggedValueFn()) .put(Timestamp.class, writeTimestampFn()) .put(UUID.class, writeUuidFn()); return protocolBuilder; } /** * Return the default printer {@link Protocol}. This is equivalent * to {@code defaultProtocolBuilder().build()}. * * @return the default printing {@link Protocol}, never null. */ public static Protocol> defaultPrinterProtocol() { return defaultProtocolBuilder().build(); } static Printer.Fn writeNullFn() { return new Printer.Fn() { @Override public void eval(Void self, Printer writer) { writer.softspace().append("nil").softspace(); } }; } static Printer.Fn> writeListFn() { return new Printer.Fn>() { @Override public void eval(List self, Printer writer) { boolean vec = self instanceof RandomAccess; writer.append(vec ? '[' : '('); for (Object o: self) { writer.printValue(o); } writer.append(vec ? ']' : ')'); } }; } static Printer.Fn> writeSetFn() { return new Printer.Fn>() { @Override public void eval(Set self, Printer writer) { writer.append("#{"); for (Object o: self) { writer.printValue(o); } writer.append('}'); } }; } static Printer.Fn> writeMapFn() { return new Printer.Fn>() { @Override public void eval(Map self, Printer writer) { writer.append('{'); for (Map.Entry p: self.entrySet()) { writer.printValue(p.getKey()) .printValue(p.getValue()); } writer.append('}'); } }; } static Printer.Fn writeKeywordFn() { return new Printer.Fn() { @Override public void eval(Keyword self, Printer writer) { writer.softspace().append(self.toString()).softspace(); } }; } static Printer.Fn writeSymbolFn() { return new Printer.Fn() { @Override public void eval(Symbol self, Printer writer) { writer.softspace().append(self.toString()).softspace(); } }; } static Printer.Fn writeTaggedValueFn() { return new Printer.Fn() { @Override public void eval(TaggedValue self, Printer writer) { writer.printValue(self.getTag()).printValue(self.getValue()); } }; } static Printer.Fn writeBooleanFn() { return new Printer.Fn() { @Override public void eval(Boolean self, Printer writer) { writer.softspace() .append(self ? "true" : "false") .softspace(); } }; } static Printer.Fn writeCharSequenceFn() { return new Printer.Fn() { @Override public void eval(CharSequence self, Printer writer) { writer.append('"'); for (int i = 0; i < self.length(); i++) { final char c = self.charAt(i); switch (c) { case '"': writer.append('\\').append('"'); break; case '\b': writer.append('\\').append('b'); break; case '\t': writer.append('\\').append('t'); break; case '\n': writer.append('\\').append('n'); break; case '\r': writer.append('\\').append('r'); break; case '\f': writer.append('\\').append('f'); break; case '\\': writer.append('\\').append('\\'); break; default: writer.append(c); } } writer.append('"'); } }; } static Printer.Fn writeCharacterFn() { return new Printer.Fn() { @Override public void eval(Character self, Printer writer) { final char c = self; if (!CharClassify.isWhitespace(c)) { writer.append('\\').append(c); } else { switch (c) { case '\b': writer.append("\\backspace"); break; case '\t': writer.append("\\tab"); break; case '\n': writer.append("\\newline"); break; case '\r': writer.append("\\return"); break; case '\f': writer.append("\\formfeed"); break; case ' ': writer.append("\\space"); break; default: throw new EdnException("Whitespace character 0x" + Integer.toHexString(c) + " is unsupported."); } } writer.softspace(); } }; } static Printer.Fn writeLongValueFn() { return new Printer.Fn() { @Override public void eval(Number self, Printer writer) { writer.softspace() .append(String.valueOf(self.longValue())) .softspace(); } }; } static Printer.Fn writeBigIntegerFn() { return new Printer.Fn() { @Override public void eval(BigInteger self, Printer writer) { writer.softspace() .append(self.toString()).append('N') .softspace(); } }; } static Printer.Fn writeDoubleValueFn() { return new Printer.Fn() { @Override public void eval(Number self, Printer writer) { writer.softspace() .append(String.valueOf(self.doubleValue())) .softspace(); } }; } static Printer.Fn writeBigDecimalFn() { return new Printer.Fn() { @Override public void eval(BigDecimal self, Printer writer) { writer.softspace() .append(self.toString()).append('M') .softspace(); } }; } static Printer.Fn writeUuidFn() { return new Printer.Fn() { @Override public void eval(UUID self, Printer writer) { writer.printValue(Parser.Config.EDN_UUID) .printValue(self.toString()); } }; } static Printer.Fn writeDateFn() { return new Printer.Fn() { @Override public void eval(Date self, Printer writer) { writer.printValue(Parser.Config.EDN_INSTANT) .printValue(InstantUtils.dateToString(self)); } }; } static Printer.Fn writeTimestampFn() { return new Printer.Fn() { @Override public void eval(Timestamp self, Printer writer) { writer.printValue(Parser.Config.EDN_INSTANT) .printValue(InstantUtils.timestampToString(self)); } }; } static Printer.Fn writeCalendarFn() { return new Printer.Fn() { @Override public void eval(GregorianCalendar self, Printer writer) { writer.printValue(Parser.Config.EDN_INSTANT) .printValue(InstantUtils.calendarToString(self)); } }; } static Printer.Fn writeTagFn() { return new Printer.Fn() { @Override public void eval(Tag self, Printer writer) { writer.softspace().append(self.toString()).softspace(); } }; } }