package com.fasterxml.jackson.databind.deser;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.format.*;
import com.fasterxml.jackson.core.io.MergedStream;
import com.fasterxml.jackson.databind.*;
Alternative to DataFormatDetector
that needs to be used when using data-binding. Since: 2.1
/**
* Alternative to {@link DataFormatDetector} that needs to be used when
* using data-binding.
*
* @since 2.1
*/
public class DataFormatReaders
{
By default we will look ahead at most 64 bytes; in most cases,
much less (4 bytes or so) is needed, but we will allow bit more
leniency to support data formats that need more complex heuristics.
/**
* By default we will look ahead at most 64 bytes; in most cases,
* much less (4 bytes or so) is needed, but we will allow bit more
* leniency to support data formats that need more complex heuristics.
*/
public final static int DEFAULT_MAX_INPUT_LOOKAHEAD = 64;
Ordered list of readers which both represent data formats to
detect (in precedence order, starting with highest) and contain
factories used for actual detection.
/**
* Ordered list of readers which both represent data formats to
* detect (in precedence order, starting with highest) and contain
* factories used for actual detection.
*/
protected final ObjectReader[] _readers;
Strength of match we consider to be good enough to be used without checking any other formats. Default value is MatchStrength.SOLID_MATCH
, /**
* Strength of match we consider to be good enough to be used
* without checking any other formats.
* Default value is {@link MatchStrength#SOLID_MATCH},
*/
protected final MatchStrength _optimalMatch;
Strength of minimal match we accept as the answer, unless better matches are found. Default value is MatchStrength.WEAK_MATCH
, /**
* Strength of minimal match we accept as the answer, unless
* better matches are found.
* Default value is {@link MatchStrength#WEAK_MATCH},
*/
protected final MatchStrength _minimalMatch;
Maximum number of leading bytes of the input that we can read
to determine data format.
Default value is DEFAULT_MAX_INPUT_LOOKAHEAD
.
/**
* Maximum number of leading bytes of the input that we can read
* to determine data format.
*<p>
* Default value is {@link #DEFAULT_MAX_INPUT_LOOKAHEAD}.
*/
protected final int _maxInputLookahead;
/*
/**********************************************************
/* Construction
/**********************************************************
*/
public DataFormatReaders(ObjectReader... detectors) {
this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH,
DEFAULT_MAX_INPUT_LOOKAHEAD);
}
public DataFormatReaders(Collection<ObjectReader> detectors) {
this(detectors.toArray(new ObjectReader[detectors.size()]));
}
private DataFormatReaders(ObjectReader[] detectors,
MatchStrength optMatch, MatchStrength minMatch,
int maxInputLookahead)
{
_readers = detectors;
_optimalMatch = optMatch;
_minimalMatch = minMatch;
_maxInputLookahead = maxInputLookahead;
}
/*
/**********************************************************
/* Fluent factories for changing match settings
/**********************************************************
*/
public DataFormatReaders withOptimalMatch(MatchStrength optMatch) {
if (optMatch == _optimalMatch) {
return this;
}
return new DataFormatReaders(_readers, optMatch, _minimalMatch, _maxInputLookahead);
}
public DataFormatReaders withMinimalMatch(MatchStrength minMatch) {
if (minMatch == _minimalMatch) {
return this;
}
return new DataFormatReaders(_readers, _optimalMatch, minMatch, _maxInputLookahead);
}
public DataFormatReaders with(ObjectReader[] readers) {
return new DataFormatReaders(readers, _optimalMatch, _minimalMatch, _maxInputLookahead);
}
public DataFormatReaders withMaxInputLookahead(int lookaheadBytes)
{
if (lookaheadBytes == _maxInputLookahead) {
return this;
}
return new DataFormatReaders(_readers, _optimalMatch, _minimalMatch, lookaheadBytes);
}
/*
/**********************************************************
/* Fluent factories for changing underlying readers
/**********************************************************
*/
public DataFormatReaders with(DeserializationConfig config)
{
final int len = _readers.length;
ObjectReader[] r = new ObjectReader[len];
for (int i = 0; i < len; ++i) {
r[i] = _readers[i].with(config);
}
return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead);
}
public DataFormatReaders withType(JavaType type)
{
final int len = _readers.length;
ObjectReader[] r = new ObjectReader[len];
for (int i = 0; i < len; ++i) {
r[i] = _readers[i].forType(type);
}
return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead);
}
/*
/**********************************************************
/* Public API
/**********************************************************
*/
Method to call to find format that content (accessible via given InputStream
) given has, as per configuration of this detector instance. Returns: Matcher object which contains result; never null, even in cases
where no match (with specified minimal match strength) is found.
/**
* Method to call to find format that content (accessible via given
* {@link InputStream}) given has, as per configuration of this detector
* instance.
*
* @return Matcher object which contains result; never null, even in cases
* where no match (with specified minimal match strength) is found.
*/
public Match findFormat(InputStream in) throws IOException
{
return _findFormat(new AccessorForReader(in, new byte[_maxInputLookahead]));
}
Method to call to find format that given content (full document)
has, as per configuration of this detector instance.
Returns: Matcher object which contains result; never null, even in cases
where no match (with specified minimal match strength) is found.
/**
* Method to call to find format that given content (full document)
* has, as per configuration of this detector instance.
*
* @return Matcher object which contains result; never null, even in cases
* where no match (with specified minimal match strength) is found.
*/
public Match findFormat(byte[] fullInputData) throws IOException
{
return _findFormat(new AccessorForReader(fullInputData));
}
Method to call to find format that given content (full document)
has, as per configuration of this detector instance.
Returns: Matcher object which contains result; never null, even in cases
where no match (with specified minimal match strength) is found. Since: 2.1
/**
* Method to call to find format that given content (full document)
* has, as per configuration of this detector instance.
*
* @return Matcher object which contains result; never null, even in cases
* where no match (with specified minimal match strength) is found.
*
* @since 2.1
*/
public Match findFormat(byte[] fullInputData, int offset, int len) throws IOException
{
return _findFormat(new AccessorForReader(fullInputData, offset, len));
}
/*
/**********************************************************
/* Overrides
/**********************************************************
*/
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append('[');
final int len = _readers.length;
if (len > 0) {
sb.append(_readers[0].getFactory().getFormatName());
for (int i = 1; i < len; ++i) {
sb.append(", ");
sb.append(_readers[i].getFactory().getFormatName());
}
}
sb.append(']');
return sb.toString();
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
private Match _findFormat(AccessorForReader acc) throws IOException
{
ObjectReader bestMatch = null;
MatchStrength bestMatchStrength = null;
for (ObjectReader f : _readers) {
acc.reset();
MatchStrength strength = f.getFactory().hasFormat(acc);
// if not better than what we have so far (including minimal level limit), skip
if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) {
continue;
}
// also, needs to better match than before
if (bestMatch != null) {
if (bestMatchStrength.ordinal() >= strength.ordinal()) {
continue;
}
}
// finally: if it's good enough match, we are done
bestMatch = f;
bestMatchStrength = strength;
if (strength.ordinal() >= _optimalMatch.ordinal()) {
break;
}
}
return acc.createMatcher(bestMatch, bestMatchStrength);
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
We need sub-class here as well, to be able to access efficiently.
/**
* We need sub-class here as well, to be able to access efficiently.
*/
protected class AccessorForReader extends InputAccessor.Std
{
public AccessorForReader(InputStream in, byte[] buffer) {
super(in, buffer);
}
public AccessorForReader(byte[] inputDocument) {
super(inputDocument);
}
public AccessorForReader(byte[] inputDocument, int start, int len) {
super(inputDocument, start, len);
}
public Match createMatcher(ObjectReader match, MatchStrength matchStrength)
{
return new Match(_in, _buffer, _bufferedStart, (_bufferedEnd - _bufferedStart),
match, matchStrength);
}
}
Result class, similar to DataFormatMatcher
/**
* Result class, similar to {@link DataFormatMatcher}
*/
public static class Match
{
protected final InputStream _originalStream;
Content read during format matching process
/**
* Content read during format matching process
*/
protected final byte[] _bufferedData;
Pointer to the first byte in buffer available for reading
/**
* Pointer to the first byte in buffer available for reading
*/
protected final int _bufferedStart;
Number of bytes available in buffer.
/**
* Number of bytes available in buffer.
*/
protected final int _bufferedLength;
Factory that produced sufficient match (if any)
/**
* Factory that produced sufficient match (if any)
*/
protected final ObjectReader _match;
Strength of match with _match
/**
* Strength of match with {@link #_match}
*/
protected final MatchStrength _matchStrength;
protected Match(InputStream in, byte[] buffered,
int bufferedStart, int bufferedLength,
ObjectReader match, MatchStrength strength)
{
_originalStream = in;
_bufferedData = buffered;
_bufferedStart = bufferedStart;
_bufferedLength = bufferedLength;
_match = match;
_matchStrength = strength;
}
/*
/**********************************************************
/* Public API, simple accessors
/**********************************************************
*/
Accessor to use to see if any formats matched well enough with
the input data.
/**
* Accessor to use to see if any formats matched well enough with
* the input data.
*/
public boolean hasMatch() { return _match != null; }
Method for accessing strength of the match, if any; if no match, will return MatchStrength.INCONCLUSIVE
. /**
* Method for accessing strength of the match, if any; if no match,
* will return {@link MatchStrength#INCONCLUSIVE}.
*/
public MatchStrength getMatchStrength() {
return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength;
}
Accessor for JsonFactory
that represents format that data matched. /**
* Accessor for {@link JsonFactory} that represents format that data matched.
*/
public ObjectReader getReader() { return _match; }
Accessor for getting brief textual name of matched format if any (null
if none). Equivalent to:
return hasMatch() ? getMatch().getFormatName() : null;
/**
* Accessor for getting brief textual name of matched format if any (null
* if none). Equivalent to:
*<pre>
* return hasMatch() ? getMatch().getFormatName() : null;
*</pre>
*/
public String getMatchedFormatName() {
return _match.getFactory().getFormatName();
}
/*
/**********************************************************
/* Public API, factory methods
/**********************************************************
*/
Convenience method for trying to construct a JsonParser
for parsing content which is assumed to be in detected data format. If no match was found, returns null. /**
* Convenience method for trying to construct a {@link JsonParser} for
* parsing content which is assumed to be in detected data format.
* If no match was found, returns null.
*/
public JsonParser createParserWithMatch() throws IOException
{
if (_match == null) {
return null;
}
JsonFactory jf = _match.getFactory();
if (_originalStream == null) {
return jf.createParser(_bufferedData, _bufferedStart, _bufferedLength);
}
return jf.createParser(getDataStream());
}
Method to use for accessing input for which format detection has been done.
This must be used instead of using stream passed to detector
unless given stream itself can do buffering.
Stream will return all content that was read during matching process, as well
as remaining contents of the underlying stream.
/**
* Method to use for accessing input for which format detection has been done.
* This <b>must</b> be used instead of using stream passed to detector
* unless given stream itself can do buffering.
* Stream will return all content that was read during matching process, as well
* as remaining contents of the underlying stream.
*/
public InputStream getDataStream() {
if (_originalStream == null) {
return new ByteArrayInputStream(_bufferedData, _bufferedStart, _bufferedLength);
}
return new MergedStream(null, _originalStream, _bufferedData, _bufferedStart, _bufferedLength);
}
}
}