diff --git a/yaml/pom.xml b/yaml/pom.xml index 863f190da..637a07c7d 100644 --- a/yaml/pom.xml +++ b/yaml/pom.xml @@ -28,9 +28,9 @@ - org.yaml - snakeyaml - 1.23 + org.snakeyaml + snakeyaml-engine + 1.0 diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/WriterWrapper.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/WriterWrapper.java new file mode 100644 index 000000000..95751de28 --- /dev/null +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/WriterWrapper.java @@ -0,0 +1,41 @@ +package com.fasterxml.jackson.dataformat.yaml; + +import java.io.IOException; +import java.io.Writer; + +import org.snakeyaml.engine.v1.api.StreamDataWriter; + +public class WriterWrapper implements StreamDataWriter { + private final Writer _writer; + + public WriterWrapper(Writer _writer) { + this._writer = _writer; + } + + @Override + public void flush() { + try { + _writer.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void write(String str) { + try { + _writer.write(str); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void write(String str, int off, int len) { + try { + _writer.write(str, off, len); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java index 94b42cec6..0dac0ac8e 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java @@ -3,7 +3,7 @@ import java.io.*; import java.nio.charset.Charset; -import org.yaml.snakeyaml.DumperOptions; +import org.snakeyaml.engine.v1.common.SpecVersion; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.base.TextualTSFactory; @@ -42,7 +42,7 @@ public class YAMLFactory /********************************************************************** */ - protected DumperOptions.Version _version; // enum, is serializable + protected SpecVersion _version; // enum, is serializable /** * Default constructor used to create factory instances. diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java index a45e9081e..4cba15c91 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java @@ -6,13 +6,28 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; +import java.util.Optional; import java.util.regex.Pattern; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.DumperOptions.FlowStyle; -import org.yaml.snakeyaml.emitter.Emitter; -import org.yaml.snakeyaml.events.*; -import org.yaml.snakeyaml.nodes.Tag; +import org.snakeyaml.engine.v1.api.DumpSettings; +import org.snakeyaml.engine.v1.api.DumpSettingsBuilder; +import org.snakeyaml.engine.v1.common.Anchor; +import org.snakeyaml.engine.v1.common.FlowStyle; +import org.snakeyaml.engine.v1.common.ScalarStyle; +import org.snakeyaml.engine.v1.common.SpecVersion; +import org.snakeyaml.engine.v1.emitter.Emitter; +import org.snakeyaml.engine.v1.events.AliasEvent; +import org.snakeyaml.engine.v1.events.DocumentEndEvent; +import org.snakeyaml.engine.v1.events.DocumentStartEvent; +import org.snakeyaml.engine.v1.events.ImplicitTuple; +import org.snakeyaml.engine.v1.events.MappingEndEvent; +import org.snakeyaml.engine.v1.events.MappingStartEvent; +import org.snakeyaml.engine.v1.events.ScalarEvent; +import org.snakeyaml.engine.v1.events.SequenceEndEvent; +import org.snakeyaml.engine.v1.events.SequenceStartEvent; +import org.snakeyaml.engine.v1.events.StreamEndEvent; +import org.snakeyaml.engine.v1.events.StreamStartEvent; +import org.snakeyaml.engine.v1.nodes.Tag; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.base.GeneratorBase; @@ -97,7 +112,12 @@ public enum Feature implements FormatFeature * If disabled, Unix linefeed ({@code \n}) will be used. *

* Default value is `false` for backwards compatibility. + * + * This setting does not do anything. Regardless of its value, SnakeYAML Engine will use the line break defined + * in System.getProperty("line.separator") + * @deprecated */ + @Deprecated USE_PLATFORM_LINE_BREAKS(false), /** @@ -168,23 +188,23 @@ private Feature(boolean defaultState) { protected Writer _writer; - protected DumperOptions _outputOptions; + protected DumpSettings _outputOptions; // for field names, leave out quotes - private final static DumperOptions.ScalarStyle STYLE_NAME = DumperOptions.ScalarStyle.PLAIN; + private final static ScalarStyle STYLE_NAME = ScalarStyle.PLAIN; // numbers, booleans, should use implicit - private final static DumperOptions.ScalarStyle STYLE_SCALAR = DumperOptions.ScalarStyle.PLAIN; + private final static ScalarStyle STYLE_SCALAR = ScalarStyle.PLAIN; // Strings quoted for fun - private final static DumperOptions.ScalarStyle STYLE_QUOTED = DumperOptions.ScalarStyle.DOUBLE_QUOTED; + private final static ScalarStyle STYLE_QUOTED = ScalarStyle.DOUBLE_QUOTED; // Strings in literal (block) style - private final static DumperOptions.ScalarStyle STYLE_LITERAL = DumperOptions.ScalarStyle.LITERAL; + private final static ScalarStyle STYLE_LITERAL = ScalarStyle.LITERAL; // Which flow style to use for Base64? Maybe basic quoted? // 29-Nov-2017, tatu: Actually SnakeYAML uses block style so: - private final static DumperOptions.ScalarStyle STYLE_BASE64 = STYLE_LITERAL; + private final static ScalarStyle STYLE_BASE64 = STYLE_LITERAL; - private final static DumperOptions.ScalarStyle STYLE_PLAIN = DumperOptions.ScalarStyle.PLAIN; + private final static ScalarStyle STYLE_PLAIN = ScalarStyle.PLAIN; /* /********************************************************************** @@ -215,7 +235,7 @@ private Feature(boolean defaultState) { public YAMLGenerator(ObjectWriteContext writeContext, IOContext ioCtxt, int streamWriteFeatures, int yamlFeatures, Writer out, - org.yaml.snakeyaml.DumperOptions.Version version) + SpecVersion version) throws IOException { super(writeContext, streamWriteFeatures); @@ -225,22 +245,22 @@ public YAMLGenerator(ObjectWriteContext writeContext, IOContext ioCtxt, _outputOptions = buildDumperOptions(streamWriteFeatures, yamlFeatures, version); - _emitter = new Emitter(_writer, _outputOptions); + _emitter = new Emitter( _outputOptions, new WriterWrapper(_writer)); // should we start output now, or try to defer? - _emitter.emit(new StreamStartEvent(null, null)); + _emitter.emit(new StreamStartEvent()); Map noTags = Collections.emptyMap(); boolean startMarker = Feature.WRITE_DOC_START_MARKER.enabledIn(yamlFeatures); - _emitter.emit(new DocumentStartEvent(null, null, startMarker, - version, // for 1.10 was: ((version == null) ? null : version.getArray()), + _emitter.emit(new DocumentStartEvent(startMarker, Optional.empty(), + // for 1.10 was: ((version == null) ? null : version.getArray()), noTags)); } - protected DumperOptions buildDumperOptions(int streamWriteFeatures, int yamlFeatures, - org.yaml.snakeyaml.DumperOptions.Version version) + protected DumpSettings buildDumperOptions(int streamWriteFeatures, int yamlFeatures, + SpecVersion version) { - DumperOptions opt = new DumperOptions(); + DumpSettingsBuilder opt = new DumpSettingsBuilder(); // would we want canonical? if (Feature.CANONICAL_OUTPUT.enabledIn(_formatWriteFeatures)) { opt.setCanonical(true); @@ -260,11 +280,7 @@ protected DumperOptions buildDumperOptions(int streamWriteFeatures, int yamlFeat opt.setIndicatorIndent(1); opt.setIndent(2); } - // 14-May-2018: [dataformats-text#84] allow use of platform linefeed - if (Feature.USE_PLATFORM_LINE_BREAKS.enabledIn(_formatWriteFeatures)) { - opt.setLineBreak(DumperOptions.LineBreak.getPlatformLineBreak()); - } - return opt; + return opt.build(); } /* @@ -425,8 +441,8 @@ public final void flush() throws IOException public void close() throws IOException { if (!isClosed()) { - _emitter.emit(new DocumentEndEvent(null, null, false)); - _emitter.emit(new StreamEndEvent(null, null)); + _emitter.emit(new DocumentEndEvent( false)); + _emitter.emit(new StreamEndEvent()); super.close(); /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() @@ -460,12 +476,12 @@ public final void writeStartArray() throws IOException FlowStyle style = _outputOptions.getDefaultFlowStyle(); String yamlTag = _typeId; boolean implicit = (yamlTag == null); - String anchor = _objectId; - if (anchor != null) { + Optional anchor = Optional.ofNullable(_objectId).map(s -> new Anchor(s)); + if (anchor.isPresent()) { _objectId = null; } - _emitter.emit(new SequenceStartEvent(anchor, yamlTag, - implicit, null, null, style)); + _emitter.emit(new SequenceStartEvent(anchor, Optional.ofNullable(yamlTag), + implicit, style)); } @Override @@ -477,7 +493,7 @@ public final void writeEndArray() throws IOException // just to make sure we don't "leak" type ids _typeId = null; _outputContext = _outputContext.getParent(); - _emitter.emit(new SequenceEndEvent(null, null)); + _emitter.emit(new SequenceEndEvent()); } @Override @@ -488,12 +504,11 @@ public final void writeStartObject() throws IOException FlowStyle style = _outputOptions.getDefaultFlowStyle(); String yamlTag = _typeId; boolean implicit = (yamlTag == null); - String anchor = _objectId; - if (anchor != null) { + Optional anchor = Optional.ofNullable(_objectId).map(s -> new Anchor(s)); + if (anchor.isPresent()) { _objectId = null; } - _emitter.emit(new MappingStartEvent(anchor, yamlTag, - implicit, null, null, style)); + _emitter.emit(new MappingStartEvent(anchor, Optional.ofNullable(yamlTag), implicit, style)); } @Override @@ -505,7 +520,7 @@ public final void writeEndObject() throws IOException // just to make sure we don't "leak" type ids _typeId = null; _outputContext = _outputContext.getParent(); - _emitter.emit(new MappingEndEvent(null, null)); + _emitter.emit(new MappingEndEvent()); } /* @@ -522,7 +537,7 @@ public void writeString(String text) throws IOException,JsonGenerationException return; } _verifyValueWrite("write String value"); - DumperOptions.ScalarStyle style = STYLE_QUOTED; + ScalarStyle style = STYLE_QUOTED; if (Feature.MINIMIZE_QUOTES.enabledIn(_formatWriteFeatures) && !isBooleanContent(text)) { // If this string could be interpreted as a number, it must be quoted. if (Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS.enabledIn(_formatWriteFeatures) @@ -752,7 +767,7 @@ public void writeObjectRef(Object id) throws IOException { _verifyValueWrite("write Object reference"); - AliasEvent evt = new AliasEvent(String.valueOf(id), null, null); + AliasEvent evt = new AliasEvent(Optional.of(String.valueOf(id)).map(s -> new Anchor(s))); _emitter.emit(evt); } @@ -797,7 +812,7 @@ protected void _releaseBuffers() { // ... and sometimes we specifically DO want explicit tag: private final static ImplicitTuple EXPLICIT_TAGS = new ImplicitTuple(false, false); - protected void _writeScalar(String value, String type, DumperOptions.ScalarStyle style) throws IOException + protected void _writeScalar(String value, String type, ScalarStyle style) throws IOException { _emitter.emit(_scalarEvent(value, style)); } @@ -812,27 +827,26 @@ private void _writeScalarBinary(Base64Variant b64variant, } final String lf = _lf(); String encoded = b64variant.encode(data, false, lf); - _emitter.emit(new ScalarEvent(null, TAG_BINARY, EXPLICIT_TAGS, encoded, - null, null, STYLE_BASE64)); + _emitter.emit(new ScalarEvent(Optional.empty(), Optional.ofNullable(TAG_BINARY), EXPLICIT_TAGS, encoded, STYLE_BASE64)); + } - protected ScalarEvent _scalarEvent(String value, DumperOptions.ScalarStyle style) + protected ScalarEvent _scalarEvent(String value, ScalarStyle style) { String yamlTag = _typeId; if (yamlTag != null) { _typeId = null; } - String anchor = _objectId; - if (anchor != null) { + Optional anchor = Optional.ofNullable(_objectId).map(s -> new Anchor(s)); + if (anchor.isPresent()) { _objectId = null; } // 29-Nov-2017, tatu: Not 100% sure why we don't force explicit tags for // type id, but trying to do so seems to double up tag output... - return new ScalarEvent(anchor, yamlTag, NO_TAGS, value, - null, null, style); + return new ScalarEvent(anchor, Optional.ofNullable(yamlTag), NO_TAGS, value, style); } protected String _lf() { - return _outputOptions.getLineBreak().getString(); + return _outputOptions.getBestLineBreak(); } } diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java index eeed28946..996c6a08f 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java @@ -3,17 +3,26 @@ import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Optional; import java.util.regex.Pattern; -import org.yaml.snakeyaml.error.Mark; -import org.yaml.snakeyaml.events.*; -import org.yaml.snakeyaml.nodes.NodeId; -import org.yaml.snakeyaml.nodes.Tag; -import org.yaml.snakeyaml.parser.ParserImpl; -import org.yaml.snakeyaml.reader.StreamReader; -import org.yaml.snakeyaml.resolver.Resolver; - import com.fasterxml.jackson.core.*; +import org.snakeyaml.engine.v1.api.LoadSettings; +import org.snakeyaml.engine.v1.api.LoadSettingsBuilder; +import org.snakeyaml.engine.v1.common.Anchor; +import org.snakeyaml.engine.v1.events.AliasEvent; +import org.snakeyaml.engine.v1.events.CollectionStartEvent; +import org.snakeyaml.engine.v1.events.Event; +import org.snakeyaml.engine.v1.events.MappingStartEvent; +import org.snakeyaml.engine.v1.events.NodeEvent; +import org.snakeyaml.engine.v1.events.ScalarEvent; +import org.snakeyaml.engine.v1.exceptions.Mark; +import org.snakeyaml.engine.v1.nodes.Tag; +import org.snakeyaml.engine.v1.parser.ParserImpl; +import org.snakeyaml.engine.v1.resolver.JsonScalarResolver; +import org.snakeyaml.engine.v1.resolver.ScalarResolver; +import org.snakeyaml.engine.v1.scanner.StreamReader; + import com.fasterxml.jackson.core.base.ParserBase; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.util.BufferRecycler; @@ -34,7 +43,7 @@ public enum Feature implements FormatFeature final boolean _defaultState; final int _mask; - + // Method that calculates bit set (flags) of all features that // are enabled by default. public static int collectDefaults() @@ -47,16 +56,16 @@ public static int collectDefaults() } return flags; } - + private Feature(boolean defaultState) { _defaultState = defaultState; _mask = (1 << ordinal()); } - + @Override public boolean enabledByDefault() { return _defaultState; } @Override - public boolean enabledIn(int flags) { return (flags & _mask) != 0; } + public boolean enabledIn(int flags) { return (flags & _mask) != 0; } @Override public int getMask() { return _mask; } } @@ -93,7 +102,7 @@ private Feature(boolean defaultState) { protected final Reader _reader; protected final ParserImpl _yamlParser; - protected final Resolver _yamlResolver = new Resolver(); + protected final ScalarResolver _yamlResolver = new JsonScalarResolver(); /* /********************************************************************** @@ -116,7 +125,7 @@ private Feature(boolean defaultState) { * mostly free of underscores */ protected String _cleanedTextValue; - + /** * Let's also have a local copy of the current field name */ @@ -132,21 +141,22 @@ private Feature(boolean defaultState) { * Anchor for the value that parser currently points to: in case of * structured types, value whose first token current token is. */ - protected String _currentAnchor; + protected Optional _currentAnchor; /* /********************************************************************** /* Life-cycle /********************************************************************** */ - + public YAMLParser(ObjectReadContext readCtxt, IOContext ioCtxt, BufferRecycler br, int parserFeatures, Reader reader) { - super(readCtxt, ioCtxt, parserFeatures); + super(readCtxt, ioCtxt, parserFeatures); // _formatFeatures = formatFeatures; _reader = reader; - _yamlParser = new ParserImpl(new StreamReader(reader)); + LoadSettings settings = new LoadSettingsBuilder().build();//TODO use parserFeatures + _yamlParser = new ParserImpl(new StreamReader(reader, settings), settings); } /* @@ -166,7 +176,7 @@ public boolean isCurrentAlias() { /** * Method that can be used to check if the current token has an * associated anchor (id to reference via Alias) - * + * * deprecated Since 2.3 (was added in 2.1) -- use {@link #getObjectId} instead public String getCurrentAnchor() { return _currentAnchor; @@ -247,13 +257,14 @@ public JsonLocation getCurrentLocation() { } return _locationFor(_lastEvent.getEndMark()); } - - protected JsonLocation _locationFor(Mark m) + + protected JsonLocation _locationFor(Optional option) { - if (m == null) { + if (!option.isPresent()) { return new JsonLocation(_ioContext.getSourceReference(), -1, -1, -1); } + Mark m = option.get(); return new JsonLocation(_ioContext.getSourceReference(), -1, m.getLine() + 1, // from 0- to 1-based @@ -277,16 +288,16 @@ public JsonToken nextToken() throws IOException return null; } - while (true) { + while (true /*_yamlParser.hasNext()*/) { Event evt; try { - evt = _yamlParser.getEvent(); - } catch (org.yaml.snakeyaml.error.YAMLException e) { + evt = _yamlParser.next(); + } catch (org.snakeyaml.engine.v1.exceptions.YamlEngineException e) { throw new JacksonYAMLParseException(this, e.getMessage(), e); } // is null ok? Assume it is, for now, consider to be same as end-of-doc if (evt == null) { - _currentAnchor = null; + _currentAnchor = Optional.empty(); return (_currToken = null); } _lastEvent = evt; @@ -295,10 +306,10 @@ public JsonToken nextToken() throws IOException // on values) if (_parsingContext.inObject()) { if (_currToken != JsonToken.FIELD_NAME) { - if (!evt.is(Event.ID.Scalar)) { - _currentAnchor = null; + if (evt.getEventId() != Event.ID.Scalar) { + _currentAnchor = Optional.empty(); // end is fine - if (evt.is(Event.ID.MappingEnd)) { + if (evt.getEventId() == Event.ID.MappingEnd) { if (!_parsingContext.inObject()) { // sanity check is optional, but let's do it for now _reportMismatchedEndMarker('}', ']'); } @@ -314,8 +325,8 @@ public JsonToken nextToken() throws IOException // ... not even 100% sure this is correct, or robust, but does appear to work for specific // test case given. final ScalarEvent scalar = (ScalarEvent) evt; - final String newAnchor = scalar.getAnchor(); - if ((newAnchor != null) || (_currToken != JsonToken.START_OBJECT)) { + final Optional newAnchor = scalar.getAnchor(); + if (newAnchor.isPresent() || (_currToken != JsonToken.START_OBJECT)) { _currentAnchor = scalar.getAnchor(); } final String name = scalar.getValue(); @@ -325,71 +336,70 @@ public JsonToken nextToken() throws IOException } } - _currentAnchor = null; - - // Ugh. Why not expose id, to be able to Switch? - _currentAnchor = null; - - // scalar values are probably the commonest: - if (evt.is(Event.ID.Scalar)) { - JsonToken t = _decodeScalar((ScalarEvent) evt); - _currToken = t; - return t; - } - - // followed by maps, then arrays - if (evt.is(Event.ID.MappingStart)) { - Mark m = evt.getStartMark(); - MappingStartEvent map = (MappingStartEvent) evt; - _currentAnchor = map.getAnchor(); - _parsingContext = _parsingContext.createChildObjectContext(m.getLine(), m.getColumn()); - return (_currToken = JsonToken.START_OBJECT); - } - if (evt.is(Event.ID.MappingEnd)) { // actually error; can not have map-end here - _reportError("Not expecting END_OBJECT but a value"); - } - if (evt.is(Event.ID.SequenceStart)) { - Mark m = evt.getStartMark(); - _currentAnchor = ((NodeEvent)evt).getAnchor(); - _parsingContext = _parsingContext.createChildArrayContext(m.getLine(), m.getColumn()); - return (_currToken = JsonToken.START_ARRAY); - } - if (evt.is(Event.ID.SequenceEnd)) { - if (!_parsingContext.inArray()) { // sanity check is optional, but let's do it for now - _reportMismatchedEndMarker(']', '}'); - } - _parsingContext = _parsingContext.getParent(); - return (_currToken = JsonToken.END_ARRAY); - } - - // after this, less common tokens: - - if (evt.is(Event.ID.DocumentEnd)) { - // [dataformat-yaml#72]: logical end of doc; fine. Two choices; either skip, - // or return null as marker (but do NOT close). Earlier returned `null`, but - // to allow multi-document reading should actually just skip. -// return (_currToken = null); - continue; - } - if (evt.is(Event.ID.DocumentStart)) { -// DocumentStartEvent dd = (DocumentStartEvent) evt; - // does this matter? Shouldn't, should it? - continue; - } - if (evt.is(Event.ID.Alias)) { - AliasEvent alias = (AliasEvent) evt; - _currentIsAlias = true; - _textValue = alias.getAnchor(); - _cleanedTextValue = null; - // for now, nothing to do: in future, maybe try to expose as ObjectIds? - return (_currToken = JsonToken.VALUE_STRING); - } - if (evt.is(Event.ID.StreamEnd)) { // end-of-input; force closure - close(); - return (_currToken = null); - } - if (evt.is(Event.ID.StreamStart)) { // useless, skip - continue; + _currentAnchor = Optional.empty(); + + switch (evt.getEventId()) { + case Scalar: + // scalar values are probably the commonest: + JsonToken t = _decodeScalar((ScalarEvent) evt); + _currToken = t; + return t; + case MappingStart: + // followed by maps, then arrays + Optional m = evt.getStartMark(); + MappingStartEvent map = (MappingStartEvent) evt; + _currentAnchor = map.getAnchor(); + _parsingContext = _parsingContext.createChildObjectContext( + m.map(mark -> mark.getLine()).orElse(0), m.map(mark -> mark.getColumn()).orElse(0)); + return (_currToken = JsonToken.START_OBJECT); + + case MappingEnd: + // actually error; can not have map-end here + _reportError("Not expecting END_OBJECT but a value"); + + case SequenceStart: + Optional mrk = evt.getStartMark(); + _currentAnchor = ((NodeEvent) evt).getAnchor(); + _parsingContext = _parsingContext.createChildArrayContext( + mrk.map(mark -> mark.getLine()).orElse(0), mrk.map(mark -> mark.getColumn()).orElse(0)); + return (_currToken = JsonToken.START_ARRAY); + + case SequenceEnd: + if (!_parsingContext.inArray()) { // sanity check is optional, but let's do it for now + _reportMismatchedEndMarker(']', '}'); + } + _parsingContext = _parsingContext.getParent(); + return (_currToken = JsonToken.END_ARRAY); + + // after this, less common tokens: + case DocumentEnd: + // [dataformat-yaml#72]: logical end of doc; fine. Two choices; either skip, + // or return null as marker (but do NOT close). Earlier returned `null`, but + // to allow multi-document reading should actually just skip. + // return (_currToken = null); + continue; + + case DocumentStart: + // DocumentStartEvent dd = (DocumentStartEvent) evt; + // does this matter? Shouldn't, should it? + continue; + + case Alias: + AliasEvent alias = (AliasEvent) evt; + _currentIsAlias = true; + _textValue = alias.getAnchor().orElseThrow(() -> new RuntimeException("Alias must be provided.")).getAnchor(); + _cleanedTextValue = null; + // for now, nothing to do: in future, maybe try to expose as ObjectIds? + return (_currToken = JsonToken.VALUE_STRING); + + case StreamEnd: + // end-of-input; force closure + close(); + return (_currToken = null); + + case StreamStart: + // useless, skip + continue; } } } @@ -400,11 +410,11 @@ protected JsonToken _decodeScalar(ScalarEvent scalar) throws IOException _textValue = value; _cleanedTextValue = null; // we may get an explicit tag, if so, use for corroborating... - String typeTag = scalar.getTag(); + Optional typeTagOptional = scalar.getTag(); final int len = value.length(); - if (typeTag == null || typeTag.equals("!")) { // no, implicit - Tag nodeTag = _yamlResolver.resolve(NodeId.scalar, value, scalar.getImplicit().canOmitTagInPlainScalar()); + if (!typeTagOptional.isPresent() || typeTagOptional.get().equals("!")) { // no, implicit + Tag nodeTag = _yamlResolver.resolve(value, scalar.getImplicit().canOmitTagInPlainScalar()); if (nodeTag == Tag.STR) { return JsonToken.VALUE_STRING; } @@ -427,6 +437,7 @@ protected JsonToken _decodeScalar(ScalarEvent scalar) throws IOException return JsonToken.VALUE_STRING; } } else { // yes, got type tag + String typeTag = typeTagOptional.get(); if (typeTag.startsWith("tag:yaml.org,2002:")) { typeTag = typeTag.substring("tag:yaml.org,2002:".length()); if (typeTag.contains(",")) { @@ -466,7 +477,7 @@ protected JsonToken _decodeScalar(ScalarEvent scalar) throws IOException } } } - + // any way to figure out actual type? No? return JsonToken.VALUE_STRING; } @@ -474,21 +485,8 @@ protected JsonToken _decodeScalar(ScalarEvent scalar) throws IOException protected Boolean _matchYAMLBoolean(String value, int len) { switch (len) { - case 1: - switch (value.charAt(0)) { - case 'y': case 'Y': return Boolean.TRUE; - case 'n': case 'N': return Boolean.FALSE; - } - break; - case 2: - if ("no".equalsIgnoreCase(value)) return Boolean.FALSE; - if ("on".equalsIgnoreCase(value)) return Boolean.TRUE; - break; - case 3: - if ("yes".equalsIgnoreCase(value)) return Boolean.TRUE; - if ("off".equalsIgnoreCase(value)) return Boolean.FALSE; - break; case 4: + //TODO it should be only lower case if ("true".equalsIgnoreCase(value)) return Boolean.TRUE; break; case 5: @@ -553,11 +551,11 @@ protected JsonToken _decodeNumberScalar(String value, final int len) _numTypesValid = 0; return _cleanYamlFloat(_textValue); } - + // 25-Aug-2016, tatu: If we can't actually match it to valid number, // consider String; better than claiming there's not toekn return JsonToken.VALUE_STRING; - } + } protected JsonToken _decodeIntWithUnderscores(String value, final int len) { @@ -575,7 +573,7 @@ protected JsonToken _decodeIntWithUnderscores(String value, final int len) public boolean hasTextCharacters() { return false; } - + @Override public String getText() throws IOException { @@ -639,7 +637,7 @@ public int getText(Writer writer) throws IOException @Override public Object getEmbeddedObject() throws IOException { - if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) { + if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) { return _binaryValue; } return null; @@ -661,7 +659,7 @@ public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IO /* Number accessor overrides /********************************************************************** */ - + @Override protected void _parseNumericValue(int expType) throws IOException { @@ -713,7 +711,7 @@ protected void _parseNumericValue(int expType) throws IOException } catch (NumberFormatException nex) { // NOTE: pass non-cleaned variant for error message // Can this ever occur? Due to overflow, maybe? - _wrapError("Malformed numeric value '"+_textValue+"'", nex); + _wrapError("Malformed numeric value '" + _textValue + "'", nex); } } if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { @@ -731,11 +729,11 @@ protected void _parseNumericValue(int expType) throws IOException } catch (NumberFormatException nex) { // Can this ever occur? Due to overflow, maybe? // NOTE: pass non-cleaned variant for error message - _wrapError("Malformed numeric value '"+_textValue+"'", nex); + _wrapError("Malformed numeric value '" + _textValue + "'", nex); } return; } - _reportError("Current token ("+_currToken+") not numeric, can not use numeric value accessors"); + _reportError("Current token (" + _currToken + ") not numeric, can not use numeric value accessors"); } @Override @@ -768,30 +766,31 @@ protected int _parseIntValue() throws IOException public boolean canReadObjectId() { // yup return true; } - + @Override public boolean canReadTypeId() { return true; // yes, YAML got 'em } - + @Override public String getObjectId() throws IOException { - return _currentAnchor; + return _currentAnchor.map(a -> a.getAnchor()).orElse(null); } @Override public String getTypeId() throws IOException { - String tag; + Optional tagOpt; if (_lastEvent instanceof CollectionStartEvent) { - tag = ((CollectionStartEvent) _lastEvent).getTag(); + tagOpt = ((CollectionStartEvent) _lastEvent).getTag(); } else if (_lastEvent instanceof ScalarEvent) { - tag = ((ScalarEvent) _lastEvent).getTag(); + tagOpt = ((ScalarEvent) _lastEvent).getTag(); } else { return null; } - if (tag != null) { + if (tagOpt.isPresent()) { + String tag = tagOpt.get(); // 04-Aug-2013, tatu: Looks like YAML parser's expose these in... somewhat exotic // ways sometimes. So let's prepare to peel off some wrappings: while (tag.startsWith("!")) { @@ -807,7 +806,7 @@ public String getTypeId() throws IOException /* Internal methods /********************************************************************** */ - + /** * Helper method used to clean up YAML floating-point value so it can be parsed * using standard JDK classes. diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/Mark.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/Mark.java index 1fdef64ed..a784a8998 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/Mark.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/Mark.java @@ -1,5 +1,7 @@ package com.fasterxml.jackson.dataformat.yaml.snakeyaml.error; +import java.util.Optional; + /** * Placeholder for shaded org.yaml.snakeyaml.error.Mark * @@ -10,28 +12,20 @@ @Deprecated // since 2.8 public class Mark { - protected final org.yaml.snakeyaml.error.Mark _source; + protected final org.snakeyaml.engine.v1.exceptions.Mark _source; - protected Mark(org.yaml.snakeyaml.error.Mark src) { + protected Mark(org.snakeyaml.engine.v1.exceptions.Mark src) { _source = src; } - public static Mark from(org.yaml.snakeyaml.error.Mark src) { - return (src == null) ? null : new Mark(src); + public static Mark from(Optional src) { + return (!src.isPresent()) ? null : new Mark(src.get()); } public String getName() { return _source.getName(); } - public String get_snippet() { - return _source.get_snippet(); - } - - public String get_snippet(int indent, int max_length) { - return _source.get_snippet(indent, max_length); - } - public int getColumn() { return _source.getColumn(); } diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/MarkedYAMLException.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/MarkedYAMLException.java new file mode 100644 index 000000000..e69de29bb diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/YAMLException.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/YAMLException.java index 9c0ca5804..93c016022 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/YAMLException.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/snakeyaml/error/YAMLException.java @@ -16,12 +16,12 @@ public class YAMLException extends JacksonYAMLParseException private static final long serialVersionUID = 1L; public YAMLException(JsonParser p, - org.yaml.snakeyaml.error.YAMLException src) { + org.snakeyaml.engine.v1.exceptions.YamlEngineException src) { super(p, src.getMessage(), src); } public static YAMLException from(JsonParser p, - org.yaml.snakeyaml.error.YAMLException src) { + org.snakeyaml.engine.v1.exceptions.YamlEngineException src) { return new YAMLException(p, src); } } diff --git a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ExceptionConversionTest.java b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ExceptionConversionTest.java index 200f77e71..fbef01329 100644 --- a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ExceptionConversionTest.java +++ b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ExceptionConversionTest.java @@ -14,7 +14,7 @@ public void testSimpleParsingLeakage() throws Exception try { mapper.readTree("foo:\nbar: true\n baz: false"); fail("Should not pass with invalid YAML"); - } catch (org.yaml.snakeyaml.scanner.ScannerException e) { + } catch (org.snakeyaml.engine.v1.exceptions.ScannerException e) { fail("Internal exception type: "+e); } catch (JacksonYAMLParseException e) { // as of 2.8, this is the type to expect // (subtype of JsonParseException) diff --git a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/deser/StreamingParseTest.java b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/deser/StreamingParseTest.java index 506f64fb8..623f39e60 100644 --- a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/deser/StreamingParseTest.java +++ b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/deser/StreamingParseTest.java @@ -162,8 +162,8 @@ public void testIntParsingWithLimits() throws Exception p.close(); } - // Testing addition of underscores - public void testIntParsingUnderscoresSm() throws Exception + // TODO Testing addition of underscores (It was dropped in YAML 1.2) + public void /*test*/ IntParsingUnderscoresSm() throws Exception { // First, couple of simple small values try (JsonParser p = MAPPER.createParser("num: 10_345")) { @@ -305,7 +305,7 @@ public void testDoubleParsing() throws Exception // First, test out valid use case. String YAML; - YAML = "num: +1_000.25"; // note underscores; legal in YAML apparently + YAML = "num: +1000.25"; JsonParser p = MAPPER.createParser(YAML); assertToken(JsonToken.START_OBJECT, p.nextToken()); @@ -315,16 +315,16 @@ public void testDoubleParsing() throws Exception StringWriter w = new StringWriter(); assertEquals(3, p.getText(w)); assertEquals("num", w.toString()); - + // should be considered a String... assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(1000.25, p.getDoubleValue()); // let's retain exact representation text however: - assertEquals("+1_000.25", p.getText()); + assertEquals("+1000.25", p.getText()); p.close(); - + // and then non-number that may be mistaken - + final String IP = "10.12.45.127"; YAML = "ip: "+IP+"\n"; p = MAPPER.createParser(YAML); @@ -337,7 +337,7 @@ public void testDoubleParsing() throws Exception w = new StringWriter(); assertEquals(IP.length(), p.getText(w)); assertEquals(IP, w.toString()); - + assertEquals(IP, p.getText()); p.close(); } @@ -365,7 +365,7 @@ public void testColons() throws Exception p.close(); } - + /** * How should YAML Anchors be exposed? */ @@ -418,7 +418,7 @@ public void testAnchorParsing() throws Exception assertToken(JsonToken.END_OBJECT, yp.nextToken()); assertToken(JsonToken.END_OBJECT, yp.nextToken()); - + assertNull(yp.nextToken()); yp.close(); } @@ -536,7 +536,10 @@ public void testNulls() throws Exception p.close(); } - public void testTildeNulls() throws Exception + /* + * Tilde '~' is back to string in YAML 1.2 (using the JSON schema) + */ + public void testTildeIsString() throws Exception { String YAML = "nulls: [~ ]"; JsonParser p = MAPPER.createParser(YAML); @@ -545,7 +548,7 @@ public void testTildeNulls() throws Exception assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("nulls", p.currentName()); assertToken(JsonToken.START_ARRAY, p.nextToken()); - assertToken(JsonToken.VALUE_NULL, p.nextToken()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken());