/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opentest4j;
import java.io.
Serializable;
/**
* Serializable representation of a value that was used in an assertion.
*
* <p>This class only stores the value if it implements {@link Serializable}.
* In any case, it stores its {@linkplain #getType() runtime type}, {@linkplain
* #getIdentityHashCode() identity hash code}, and {@linkplain
* #getStringRepresentation() string representation} determined via {@link
* String#valueOf(Object)}. If the invocation of {@code String.valueOf(Object)}
* throws an {@link Exception}, the string representation will take the form of
* {@code "<Exception in toString(): " + e + ">"}, where "e" is the caught
* exception.
*
* <p>The {@link #toString()} method returns the string representation of the
* value along with its type and identity hash code.
*
* @author Marc Philipp
* @author Sam Brannen
* @since 1.0
* @see System#identityHashCode
*/
public final class
ValueWrapper implements
Serializable {
private static final long
serialVersionUID = 1L;
private static final
ValueWrapper nullValueWrapper = new
ValueWrapper(null);
/**
* Factory for creating a new {@code ValueWrapper} for the supplied {@code value}.
*
* <p>If the supplied {@code value} is {@code null}, this method will return a
* cached {@code ValueWrapper} suitable for all {@code null} values.
* If the supplied {@code value} is already an instance of {@link ValueWrapper},
* it will be returned as is.
*
* @param value the value to wrap; may be {@code null}
* @return a wrapper for the supplied value; never {@code null}
*/
public static
ValueWrapper create(
Object value) {
if (
value instanceof
ValueWrapper)
return (
ValueWrapper)
value;
return (
value == null) ?
nullValueWrapper : new
ValueWrapper(
value);
}
/**
* Factory for creating a new {@code ValueWrapper} for the supplied {@code value}
* using the supplied custom {@code stringRepresentation}.
*
* <p>You should use this method when you don't want to rely on the result of the
* value's {@link Object#toString() toString()} method.
*
* <p>If the supplied {@code value} is {@code null}, this method will return a
* cached {@code ValueWrapper} suitable for all {@code null} values.
* If the supplied {@code value} is already an instance of {@link ValueWrapper},
* it will be returned as is if the {@code stringRepresentation} match, otherwise
* the original value will be unwrapped and a new {@code ValueWrapper} with the
* new {@code stringRepresentation} will be created.
*
* @param value the value to wrap; may be {@code null}
* @param stringRepresentation a custom rendering of the value; will fallback to
* the default behavior if {@code null}
* @return a wrapper for the supplied value; never {@code null}
* @since 1.2
*/
public static
ValueWrapper create(
Object value,
String stringRepresentation) {
if (
value instanceof
ValueWrapper) {
ValueWrapper wrapper = (
ValueWrapper)
value;
return
wrapper.
stringRepresentation.
equals(
stringRepresentation) ?
wrapper
:
create(
wrapper.
value,
stringRepresentation);
}
return (
value == null ?
nullValueWrapper : new
ValueWrapper(
value,
stringRepresentation));
}
private final
Serializable value;
private final
Class<?>
type;
private final
String stringRepresentation;
private final int
identityHashCode;
private final transient
Object ephemeralValue;
/**
* Reads and stores the supplied value's runtime type, string representation, and
* identity hash code.
*/
private
ValueWrapper(
Object value,
String stringRepresentation) {
this.
value =
value instanceof
Serializable ? (
Serializable)
value : null;
this.
type =
value != null ?
value.
getClass() : null;
this.
stringRepresentation =
stringRepresentation == null ?
safeValueToString(
value) :
stringRepresentation;
this.
identityHashCode =
System.
identityHashCode(
value);
this.
ephemeralValue =
value;
}
private
ValueWrapper(
Object value) {
this(
value,
safeValueToString(
value));
}
private static
String safeValueToString(
Object value) {
try {
return
String.
valueOf(
value);
}
catch (
Exception e) {
return "<Exception in toString(): " +
e + ">";
}
}
/**
* Returns the value supplied to {@link #create(Object)} if the value
* implements {@link Serializable}; otherwise, {@code null}.
*
* @see #getEphemeralValue()
*/
public
Serializable getValue() {
return this.
value;
}
/**
* Returns the value's runtime type or {@code null} if the value is
* {@code null}.
*/
public
Class<?>
getType() {
return this.
type;
}
/**
* Returns the value's string representation.
*
* <p>The string representation is generated by invoking
* {@link String#valueOf(Object) String.valueOf(value)} for the value
* supplied to {@link #create(Object)}.
*
* @see #getValue()
*/
public
String getStringRepresentation() {
return this.
stringRepresentation;
}
/**
* Returns the value's identity hash code.
*
* <p>The identity hash code is generated by invoking
* {@link System#identityHashCode(Object) System.identityHashCode(value)}
* for the value supplied to {@link #create(Object)}.
*
* @see #getValue()
*/
public int
getIdentityHashCode() {
return this.
identityHashCode;
}
/**
* Returns the original value supplied to {@link #create(Object) create()}.
*
* <p>If this {@code ValueWrapper} was created by deserialization this method
* returns {@code null}.
*
* @see #getValue()
* @since 1.2
*/
public
Object getEphemeralValue() {
return this.
ephemeralValue;
}
/**
* Returns the value's string representation along with its type and
* identity hash code.
*/
@
Override
public
String toString() {
if (this.
type == null) {
return "null";
}
return this.
stringRepresentation + //
" (" + this.
type.
getName() + "@" +
Integer.
toHexString(this.
identityHashCode) + ")";
}
}