org.opentest4j.ValueWrapper Maven / Gradle / Ivy
/*
* 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.
*
* 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 ""}, where "e" is the caught
* exception.
*
* 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}.
*
*
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}.
*
*
You should use this method when you don't want to rely on the result of the
* value's {@link Object#toString() toString()} method.
*
*
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 "";
}
}
/**
* 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.
*
* 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.
*
*
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()}.
*
*
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) + ")";
}
}