package com.fasterxml.jackson.databind;
import java.io.
IOException;
import java.io.
Serializable;
import java.util.*;
import com.fasterxml.jackson.
core.*;
/**
* Checked exception used to signal fatal problems with mapping of
* content.
*<p>
* One additional feature is the ability to denote relevant path
* of references (during serialization/deserialization) to help in
* troubleshooting.
*/
public class
JsonMappingException
extends
JsonProcessingException
{
private static final long
serialVersionUID = 1L;
/**
* Let's limit length of reference chain, to limit damage in cases
* of infinite recursion.
*/
final static int
MAX_REFS_TO_LIST = 1000;
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* Simple bean class used to contain references. References
* can be added to indicate execution/reference path that
* lead to the problem that caused this exception to be
* thrown.
*/
public static class
Reference implements
Serializable
{
private static final long
serialVersionUID = 1L;
/**
* Object through which reference was resolved. Can be either
* actual instance (usually the case for serialization), or
* Class (usually the case for deserialization).
*/
protected
Object _from;
/**
* Name of field (for beans) or key (for Maps) that is part
* of the reference. May be null for Collection types (which
* generally have {@link #_index} defined), or when resolving
* Map classes without (yet) having an instance to operate on.
*/
protected
String _fieldName;
/**
* Index within a {@link Collection} instance that contained
* the reference; used if index is relevant and available.
* If either not applicable, or not available, -1 is used to
* denote "not known".
*/
protected int
_index = -1;
/**
* Default constructor for deserialization/sub-classing purposes
*/
protected
Reference() { }
public
Reference(
Object from) {
_from =
from; }
public
Reference(
Object from,
String fieldName) {
_from =
from;
if (
fieldName == null) {
throw new
NullPointerException("Can not pass null fieldName");
}
_fieldName =
fieldName;
}
public
Reference(
Object from, int
index) {
_from =
from;
_index =
index;
}
public void
setFrom(
Object o) {
_from =
o; }
public void
setFieldName(
String n) {
_fieldName =
n; }
public void
setIndex(int
ix) {
_index =
ix; }
public
Object getFrom() { return
_from; }
public
String getFieldName() { return
_fieldName; }
public int
getIndex() { return
_index; }
@
Override public
String toString() {
StringBuilder sb = new
StringBuilder();
Class<?>
cls = (
_from instanceof
Class<?>) ?
((
Class<?>)
_from) :
_from.
getClass();
/* Hmmh. Although Class.getName() is mostly ok, it does look
* butt-ugly for arrays. So let's use getSimpleName() instead;
* but have to prepend package name too.
*/
Package pkg =
cls.
getPackage();
if (
pkg != null) {
sb.
append(
pkg.
getName());
sb.
append('.');
}
sb.
append(
cls.
getSimpleName());
sb.
append('[');
if (
_fieldName != null) {
sb.
append('"');
sb.
append(
_fieldName);
sb.
append('"');
} else if (
_index >= 0) {
sb.
append(
_index);
} else {
sb.
append('?');
}
sb.
append(']');
return
sb.
toString();
}
}
/*
/**********************************************************
/* State/configuration
/**********************************************************
*/
/**
* Path through which problem that triggering throwing of
* this exception was reached.
*/
protected
LinkedList<
Reference>
_path;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public
JsonMappingException(
String msg)
{
super(
msg);
}
public
JsonMappingException(
String msg,
Throwable rootCause)
{
super(
msg,
rootCause);
}
public
JsonMappingException(
String msg,
JsonLocation loc)
{
super(
msg,
loc);
}
public
JsonMappingException(
String msg,
JsonLocation loc,
Throwable rootCause)
{
super(
msg,
loc,
rootCause);
}
public static
JsonMappingException from(
JsonParser jp,
String msg)
{
return new
JsonMappingException(
msg, ((
jp == null) ? null :
jp.
getTokenLocation()));
}
public static
JsonMappingException from(
JsonParser jp,
String msg,
Throwable problem)
{
return new
JsonMappingException(
msg, ((
jp == null) ? null :
jp.
getTokenLocation()),
problem);
}
/**
* Factory method used when "upgrading" an {@link IOException} into
* {@link JsonMappingException}: usually only needed to comply with
* a signature.
*
* @since 2.1
*/
public static
JsonMappingException fromUnexpectedIOE(
IOException src)
{
return new
JsonMappingException("Unexpected IOException (of type "
+
src.
getClass().
getName()+"): "+
src.
getMessage(), (
JsonLocation)null,
src);
}
/**
* Method that can be called to either create a new JsonMappingException
* (if underlying exception is not a JsonMappingException), or augment
* given exception with given path/reference information.
*
* This version of method is called when the reference is through a
* non-indexed object, such as a Map or POJO/bean.
*/
public static
JsonMappingException wrapWithPath(
Throwable src,
Object refFrom,
String refFieldName)
{
return
wrapWithPath(
src, new
Reference(
refFrom,
refFieldName));
}
/**
* Method that can be called to either create a new JsonMappingException
* (if underlying exception is not a JsonMappingException), or augment
* given exception with given path/reference information.
*
* This version of method is called when the reference is through an
* index, which happens with arrays and Collections.
*/
public static
JsonMappingException wrapWithPath(
Throwable src,
Object refFrom,
int
index)
{
return
wrapWithPath(
src, new
Reference(
refFrom,
index));
}
/**
* Method that can be called to either create a new JsonMappingException
* (if underlying exception is not a JsonMappingException), or augment
* given exception with given path/reference information.
*/
public static
JsonMappingException wrapWithPath(
Throwable src,
Reference ref)
{
JsonMappingException jme;
if (
src instanceof
JsonMappingException) {
jme = (
JsonMappingException)
src;
} else {
String msg =
src.
getMessage();
/* Related to [JACKSON-62], let's use a more meaningful placeholder
* if all we have is null
*/
if (
msg == null ||
msg.
length() == 0) {
msg = "(was "+
src.
getClass().
getName()+")";
}
jme = new
JsonMappingException(
msg, null,
src);
}
jme.
prependPath(
ref);
return
jme;
}
/*
/**********************************************************
/* Accessors/mutators
/**********************************************************
*/
/**
* Method for accessing full structural path within type hierarchy
* down to problematic property.
*/
public
List<
Reference>
getPath()
{
if (
_path == null) {
return
Collections.
emptyList();
}
return
Collections.
unmodifiableList(
_path);
}
/**
* Method for accesing description of path that lead to the
* problem that triggered this exception
*/
public
String getPathReference()
{
return
getPathReference(new
StringBuilder()).
toString();
}
public
StringBuilder getPathReference(
StringBuilder sb)
{
_appendPathDesc(
sb);
return
sb;
}
/**
* Method called to prepend a reference information in front of
* current path
*/
public void
prependPath(
Object referrer,
String fieldName)
{
Reference ref = new
Reference(
referrer,
fieldName);
prependPath(
ref);
}
/**
* Method called to prepend a reference information in front of
* current path
*/
public void
prependPath(
Object referrer, int
index)
{
Reference ref = new
Reference(
referrer,
index);
prependPath(
ref);
}
public void
prependPath(
Reference r)
{
if (
_path == null) {
_path = new
LinkedList<
Reference>();
}
/* Also: let's not increase without bounds. Could choose either
* head or tail; tail is easier (no need to ever remove), as
* well as potentially more useful so let's use it:
*/
if (
_path.
size() <
MAX_REFS_TO_LIST) {
_path.
addFirst(
r);
}
}
/*
/**********************************************************
/* Overridden methods
/**********************************************************
*/
@
Override
public
String getLocalizedMessage() {
return
_buildMessage();
}
/**
* Method is overridden so that we can properly inject description
* of problem path, if such is defined.
*/
@
Override
public
String getMessage() {
return
_buildMessage();
}
protected
String _buildMessage()
{
/* First: if we have no path info, let's just use parent's
* definition as is
*/
String msg = super.
getMessage();
if (
_path == null) {
return
msg;
}
StringBuilder sb = (
msg == null) ? new
StringBuilder() : new
StringBuilder(
msg);
/* 18-Feb-2009, tatu: initially there was a linefeed between
* message and path reference; but unfortunately many systems
* (loggers, junit) seem to assume linefeeds are only added to
* separate stack trace.
*/
sb.
append(" (through reference chain: ");
sb =
getPathReference(
sb);
sb.
append(')');
return
sb.
toString();
}
@
Override
public
String toString()
{
return
getClass().
getName()+": "+
getMessage();
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
protected void
_appendPathDesc(
StringBuilder sb)
{
if (
_path == null) {
return;
}
Iterator<
Reference>
it =
_path.
iterator();
while (
it.
hasNext()) {
sb.
append(
it.
next().
toString());
if (
it.
hasNext()) {
sb.
append("->");
}
}
}
}