package com.jasonclawson.jackson.dataformat.hocon; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.io.Writer; import java.net.URL; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.core.format.InputAccessor; import com.fasterxml.jackson.core.format.MatchStrength; import com.fasterxml.jackson.core.io.IOContext; import com.typesafe.config.*; /** * This code was pretty much copied from the jackson YAMLFactory * @author jclawson * */ public class HoconFactory extends JsonFactory { private static final long serialVersionUID = 1L; public final static String FORMAT_NAME_HOCON = "HOCON"; private final static byte UTF8_BOM_1 = (byte) 0xEF; private final static byte UTF8_BOM_2 = (byte) 0xBB; private final static byte UTF8_BOM_3 = (byte) 0xBF; public HoconFactory() { this(null); } public HoconFactory(ObjectCodec oc) { super(oc); } public HoconFactory(HoconFactory src, ObjectCodec oc) { super(src, oc); } @Override public HoconFactory copy() { _checkInvalidCopy(HoconFactory.class); return new HoconFactory(this, null); } /* /********************************************************** /* Serializable overrides /********************************************************** */ /** * Method that we need to override to actually make restoration go * through constructors etc. * Also: must be overridden by sub-classes as well. */ @Override protected Object readResolve() { return new HoconFactory(this, _objectCodec); } /* /********************************************************** /* Versioned /********************************************************** */ // @Override // public Version version() { // return PackageVersion.VERSION; // } /* /********************************************************** /* Format detection functionality (since 1.8) /********************************************************** */ @Override public String getFormatName() { return FORMAT_NAME_HOCON; } /** * Sub-classes need to override this method (as of 1.8) */ @Override public MatchStrength hasFormat(InputAccessor acc) throws IOException { if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } byte b = acc.nextByte(); // Very first thing, a UTF-8 BOM? if (b == UTF8_BOM_1) { // yes, looks like UTF-8 BOM if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } if (acc.nextByte() != UTF8_BOM_2) { return MatchStrength.NO_MATCH; } if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } if (acc.nextByte() != UTF8_BOM_3) { return MatchStrength.NO_MATCH; } if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } b = acc.nextByte(); } if (b == '{' || Character.isLetter((char) b) || Character.isDigit((char) b)) { return MatchStrength.WEAK_MATCH; } return MatchStrength.INCONCLUSIVE; } /* /********************************************************** /* Configuration, parser settings /********************************************************** */ /* /********************************************************** /* Overridden parser factory methods (for 2.1) /********************************************************** */ @SuppressWarnings("resource") @Override public HoconTreeTraversingParser createParser(String content) throws IOException, JsonParseException { Reader r = new StringReader(content); IOContext ctxt = _createContext(r, true); // true->own, can close // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { r = _inputDecorator.decorate(ctxt, r); } return _createParser(r, ctxt); } @SuppressWarnings("resource") @Override public HoconTreeTraversingParser createParser(File f) throws IOException, JsonParseException { // choosing to support hocon include instead of inputDecorator Config resolvedConfig = ConfigFactory.parseFile(f).resolve(); return new HoconTreeTraversingParser(resolvedConfig.root(), _objectCodec); } @SuppressWarnings("resource") @Override public HoconTreeTraversingParser createParser(URL url) throws IOException, JsonParseException { // choosing to support hocon include instead of inputDecorator Config resolvedConfig = ConfigFactory.parseURL(url).resolve(); return new HoconTreeTraversingParser(resolvedConfig.root(), _objectCodec); } @SuppressWarnings("resource") @Override public HoconTreeTraversingParser createParser(InputStream in) throws IOException, JsonParseException { IOContext ctxt = _createContext(in, false); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { in = _inputDecorator.decorate(ctxt, in); } return _createParser(in, ctxt); } @SuppressWarnings("resource") @Override public JsonParser createParser(Reader r) throws IOException, JsonParseException { IOContext ctxt = _createContext(r, false); if (_inputDecorator != null) { r = _inputDecorator.decorate(ctxt, r); } return _createParser(r, ctxt); } @SuppressWarnings("resource") @Override public HoconTreeTraversingParser createParser(byte[] data) throws IOException, JsonParseException { IOContext ctxt = _createContext(data, true); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length); if (in != null) { return _createParser(in, ctxt); } } return _createParser(data, 0, data.length, ctxt); } @SuppressWarnings("resource") @Override public HoconTreeTraversingParser createParser(byte[] data, int offset, int len) throws IOException, JsonParseException { IOContext ctxt = _createContext(data, true); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { InputStream in = _inputDecorator.decorate(ctxt, data, offset, len); if (in != null) { return _createParser(in, ctxt); } } return _createParser(data, offset, len, ctxt); } /* /********************************************************** /* Overridden parser factory methods (2.0 and prior) /********************************************************** */ // remove in 2.4 @Deprecated @Override public HoconTreeTraversingParser createJsonParser(String content) throws IOException, JsonParseException { return createParser(content); } // remove in 2.4 @Deprecated @Override public HoconTreeTraversingParser createJsonParser(File f) throws IOException, JsonParseException { return createParser(f); } // remove in 2.4 @Deprecated @Override public HoconTreeTraversingParser createJsonParser(URL url) throws IOException, JsonParseException { return createParser(url); } // remove in 2.4 @Deprecated @Override public HoconTreeTraversingParser createJsonParser(InputStream in) throws IOException, JsonParseException { return createParser(in); } // remove in 2.4 @Deprecated @Override public JsonParser createJsonParser(Reader r) throws IOException, JsonParseException { return createParser(r); } // remove in 2.4 @Deprecated @Override public HoconTreeTraversingParser createJsonParser(byte[] data) throws IOException, JsonParseException { return createParser(data); } // remove in 2.4 @Deprecated @Override public HoconTreeTraversingParser createJsonParser(byte[] data, int offset, int len) throws IOException, JsonParseException { return createParser(data, offset, len); } /* /********************************************************** /* Overridden generator factory methods (2.1) /********************************************************** */ @SuppressWarnings("resource") @Override public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } @SuppressWarnings("resource") @Override public JsonGenerator createGenerator(OutputStream out) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } @SuppressWarnings("resource") @Override public JsonGenerator createGenerator(Writer out) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } /* /********************************************************** /* Overridden generator factory methods (2.0 and before) /********************************************************** */ // remove in 2.4 @Deprecated @Override public JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } // remove in 2.4 @Deprecated @Override public JsonGenerator createJsonGenerator(OutputStream out) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } // remove in 2.4 @Deprecated @Override public JsonGenerator createJsonGenerator(Writer out) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } /* /****************************************************** /* Overridden internal factory methods /****************************************************** */ //protected IOContext _createContext(Object srcRef, boolean resourceManaged) @SuppressWarnings("resource") @Override protected HoconTreeTraversingParser _createParser(InputStream in, IOContext ctxt) throws IOException, JsonParseException { Reader r = _createReader(in, null, ctxt); return _createParser(r, ctxt); } @Override protected HoconTreeTraversingParser _createParser(Reader r, IOContext ctxt) throws IOException, JsonParseException { Config resolvedConfig = ConfigFactory.parseReader(r).resolve(); return new HoconTreeTraversingParser(resolvedConfig.root(), _objectCodec); } @SuppressWarnings("resource") @Override protected HoconTreeTraversingParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException, JsonParseException { Reader r = _createReader(data, offset, len, null, ctxt); return _createParser(r, ctxt); } @Override protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } @SuppressWarnings("resource") @Deprecated @Override protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } @Override protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException { throw new UnsupportedOperationException("Generating HOCON is not supported yet"); } /* /********************************************************** /* Internal methods /********************************************************** */ protected Reader _createReader(InputStream in, JsonEncoding enc, IOContext ctxt) throws IOException { if (enc == null) { enc = JsonEncoding.UTF8; } // default to UTF-8 if encoding missing if (enc == JsonEncoding.UTF8) { boolean autoClose = ctxt.isResourceManaged() || isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE); return new UTF8Reader(in, autoClose); } return new InputStreamReader(in, enc.getJavaName()); } protected Reader _createReader(byte[] data, int offset, int len, JsonEncoding enc, IOContext ctxt) throws IOException { if (enc == null) { enc = JsonEncoding.UTF8; } // default to UTF-8 if encoding missing if (enc == null || enc == JsonEncoding.UTF8) { return new UTF8Reader(data, offset, len, true); } ByteArrayInputStream in = new ByteArrayInputStream(data, offset, len); return new InputStreamReader(in, enc.getJavaName()); } }