Skip to content

Commit

Permalink
Start work on solving #652, #658
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Mar 14, 2021
1 parent 722556b commit b89098e
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 31 deletions.
85 changes: 64 additions & 21 deletions src/main/java/com/fasterxml/jackson/core/JsonLocation.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import java.nio.charset.Charset;

import com.fasterxml.jackson.core.io.InputSourceReference;

/**
* Object that encapsulates Location information used for reporting
* parsing (or potentially generation) errors, as well as current location
Expand All @@ -15,7 +17,7 @@
public class JsonLocation
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 2L; // in 2.13

/**
* Include at most first 500 characters/bytes from contents; should be enough
Expand All @@ -33,7 +35,8 @@ public class JsonLocation
* NOTE: before 2.9, Location was given as String "N/A"; with 2.9 it was
* removed so that source should be indicated as "UNKNOWN".
*/
public final static JsonLocation NA = new JsonLocation(null, -1L, -1L, -1, -1);
public final static JsonLocation NA = new JsonLocation(InputSourceReference.unknown(),
-1L, -1L, -1, -1);

protected final long _totalBytes;
protected final long _totalChars;
Expand All @@ -42,32 +45,67 @@ public class JsonLocation
protected final int _columnNr;

/**
* Displayable description for input source: file path, URL.
*<p>
* NOTE: <code>transient</code> since 2.2 so that Location itself is Serializable.
* Reference to input source; never null (but may be that of
* {@link InputSourceReference#unknown()}).
*
* @since 2.13 (before we have {@code _sourceRef} (Object-valued)
*/
final transient Object _sourceRef;
protected final InputSourceReference _inputSource;

public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr)
public JsonLocation(InputSourceReference inputSource, long totalChars,
int lineNr, int colNr)
{
/* Unfortunately, none of legal encodings are straight single-byte
* encodings. Could determine offset for UTF-16/UTF-32, but the
* most important one is UTF-8...
* so for now, we'll just not report any real byte count
*/
this(srcRef, -1L, totalChars, lineNr, colNr);
this(inputSource, -1L, totalChars, lineNr, colNr);
}

public JsonLocation(Object sourceRef, long totalBytes, long totalChars,
public JsonLocation(InputSourceReference inputSource, long totalBytes, long totalChars,
int lineNr, int columnNr)
{
_sourceRef = sourceRef;
// 14-Mar-2021, tatu: Defensive programming, but also for convenience...
if (inputSource == null) {
inputSource = InputSourceReference.unknown();
}
_inputSource = inputSource;
_totalBytes = totalBytes;
_totalChars = totalChars;
_lineNr = lineNr;
_columnNr = columnNr;
}

@Deprecated
public JsonLocation(Object srcRef, long totalChars, int lineNr, int columnNr) {
this(_wrap(srcRef), totalChars, lineNr, columnNr);
}

@Deprecated
public JsonLocation(Object srcRef, long totalBytes, long totalChars,
int lineNr, int columnNr) {
this(_wrap(srcRef), totalBytes, totalChars, lineNr, columnNr);
}

protected static InputSourceReference _wrap(Object srcRef) {
if (srcRef instanceof InputSourceReference) {
return (InputSourceReference) srcRef;
}
return new InputSourceReference(false, srcRef);
}

/**
* Accessor for information about the original input source content is being
* read from. Returned reference is never {@code null} but may not contain
* useful information.
*<p>
* NOTE: not getter, on purpose, to avoid inlusion if serialized using
* default Jackson serializer.
*
* @return Object with information about input source.
*
* @since 2.13 (to replace {@code getSourceRef})
*/
public InputSourceReference inputSource() {
return _inputSource;
}

/**
* Reference to the original resource being read, if one available.
* For example, when a parser has been constructed by passing
Expand All @@ -77,8 +115,13 @@ public JsonLocation(Object sourceRef, long totalBytes, long totalChars,
* construct the parser instance.
*
* @return Source reference this location was constructed with, if any; {@code null} if none
*
* @deprecated Since 2.13 Use {@link #inputSource} instead
*/
public Object getSourceRef() { return _sourceRef; }
@Deprecated
public Object getSourceRef() {
return _inputSource.getSource();
}

/**
* @return Line number of the location (1-based)
Expand Down Expand Up @@ -129,7 +172,7 @@ public String sourceDescription() {
@Override
public int hashCode()
{
int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode();
int hash = (_inputSource == null) ? 1 : 2;
hash ^= _lineNr;
hash += _columnNr;
hash ^= (int) _totalChars;
Expand All @@ -145,9 +188,9 @@ public boolean equals(Object other)
if (!(other instanceof JsonLocation)) return false;
JsonLocation otherLoc = (JsonLocation) other;

if (_sourceRef == null) {
if (otherLoc._sourceRef != null) return false;
} else if (!_sourceRef.equals(otherLoc._sourceRef)) return false;
if (_inputSource == null) {
if (otherLoc._inputSource != null) return false;
} else if (!_inputSource.equals(otherLoc._inputSource)) return false;

return (_lineNr == otherLoc._lineNr)
&& (_columnNr == otherLoc._columnNr)
Expand All @@ -172,7 +215,7 @@ public String toString()

protected StringBuilder _appendSourceDesc(StringBuilder sb)
{
final Object srcRef = _sourceRef;
final Object srcRef = _inputSource.getSource();

if (srcRef == null) {
sb.append("UNKNOWN");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public abstract class GeneratorBase extends JsonGenerator
* @since 2.7.7
*/
protected final static int MAX_BIG_DECIMAL_SCALE = 9999;

/*
/**********************************************************
/* Configuration
Expand Down
149 changes: 149 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/io/InputSourceReference.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.fasterxml.jackson.core.io;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
* Abstraction that encloses information about input source (streaming or
* not) for the purpose of including pertinent information in
* location (see {@link com.fasterxml.jackson.core.JsonLocation})
* objections, most commonly to be printed out as part of {@code Exception}
* messages.
*
* @since 2.13
*/
public class InputSourceReference
// sort of: we will read back as "UNKNOWN_INPUT"
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;

protected final static InputSourceReference UNKNOWN_INPUT =
new InputSourceReference(false, null);

/**
* Reference to the actual underlying source.
*/
protected final transient Object _rawSource;

/**
* For static input sources, indicates offset from the beginning
* of static array.
* {@code -1} if not in use.
*/
protected final int _offset;

/**
* For static input sources, indicates length of content in
* the static array.
* {@code -1} if not in use.
*/
protected final int _length;

/**
* Marker flag to indicate whether included content is textual or not:
* this is taken to mean, by default, that a snippet of content may be
* displayed for exception messages.
*/
protected final boolean _textualContent;

/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/

public InputSourceReference(boolean textualContent, Object rawSource) {
this(textualContent, rawSource, -1, -1);
}

public InputSourceReference(boolean textualContent, Object rawSource,
int offset, int length)
{
_textualContent = textualContent;
_rawSource = rawSource;
_offset = offset;
_length = length;
}

/**
* Accessor for getting a placeholder for cases where actual input source
* is not known (or is not something that system wants to expose).
*
* @return Placeholder "unknown" (or "empty") instance to use instead of
* {@code null} reference
*/
public static InputSourceReference unknown() {
return UNKNOWN_INPUT;
}

/**
* Factory method for legacy code to use for constructing instances to
* input sources for which only minimal amount of information is available.
* Assumed not to contain textual content (no snippet displayed).
*
* @param rawSource Underlying raw input source
*
* @return Instance with minimal information about source (basically just
* raw source without offsets;
*/
public static InputSourceReference rawSource(Object rawSource) {
return new InputSourceReference(false, rawSource);
}

/*
/**********************************************************************
/* Serializable overrides
/**********************************************************************
*/

// For JDK serialization: can/should not retain raw source, so need
// not read or write anything

private void readObject(ObjectInputStream in) throws IOException {
// nop: but must override the method
}

private void writeObject(ObjectOutputStream out) throws IOException {
// nop: but must override the method
}

protected Object readResolve() {
return UNKNOWN_INPUT;
}

/*
/**********************************************************************
/* Basic accessors
/**********************************************************************
*/

public boolean hasTextualContent() {
return _textualContent;
}

public Object getSource() {
return _rawSource;
}

/*
/**********************************************************************
/* Standard method overrides
/**********************************************************************
*/

// Just needed for JsonLocation#equals(): although it'd seem we only need
// to care about identity, for backwards compatibility better compare
// bit more
@Override
public boolean equals(Object other)
{
if (other == this) return true;
if (other == null) return false;
if (!(other instanceof InputSourceReference)) return false;
InputSourceReference otherSrc = (InputSourceReference) other;

return _rawSource == otherSrc._rawSource;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.*;

import com.fasterxml.jackson.core.io.InputSourceReference;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;

/**
Expand Down Expand Up @@ -81,6 +82,16 @@ public void testLocation() throws Exception
jp.close();
}

public void testSourceReference() throws Exception
{
InputSourceReference ref = new InputSourceReference(true, "text");

byte[] stuff = jdkSerialize(ref);
InputSourceReference ref2 = jdkDeserialize(stuff);
assertNotNull(ref2);
assertSame(ref2, InputSourceReference.unknown());
}

public void testParseException() throws Exception
{
JsonFactory jf = new JsonFactory();
Expand Down
Loading

0 comments on commit b89098e

Please sign in to comment.