class DataEntryWrapper extends DataEntry {
    /**
     * Source DataEntry.
     */
    private final DataEntry source;

    /** Strategy for the processing of sensitive data. */
    private final ProcessSensitiveData sensitiveData;

    /**
     * Constructor.
     *
     * @param dataEntry          Instance of {@link DataEntry}.
     * @param sensitiveData      Strategy for the processing of sensitive data.
     */
    public DataEntryWrapper(
        DataEntry dataEntry,
        ProcessSensitiveData sensitiveData
    ) {
        super(
            dataEntry.cacheId(),
            dataEntry.key(),
            dataEntry.value(),
            dataEntry.op(),
            dataEntry.nearXidVersion(),
            dataEntry.writeVersion(),
            dataEntry.expireTime(),
            dataEntry.partitionId(),
            dataEntry.partitionCounter(),
            dataEntry.flags()
        );

        this.source = dataEntry;

        this.sensitiveData = sensitiveData;
    }

    /** {@inheritDoc} */
    @Override public String toString() {
        final String keyStr;
        final String valStr;
        if (source instanceof UnwrapDataEntry) {
            final UnwrapDataEntry unwrappedDataEntry = (UnwrapDataEntry)this.source;

            keyStr = toString(unwrappedDataEntry.unwrappedKey(), this.source.key());

            valStr = toString(unwrappedDataEntry.unwrappedValue(), this.source.value());
        }
        else if (source instanceof RecordDataV1Serializer.EncryptedDataEntry) {
            keyStr = "<encrypted>";

            valStr = "<encrypted>";
        }
        else {
            keyStr = toString(null, this.source.key());

            valStr = toString(null, this.source.value());
        }

        return new SB(this.source.getClass().getSimpleName())
            .a("[k = ").a(keyStr)
            .a(", v = [").a(valStr).a("]")
            .a(", super = [").a(S.toString(DataEntry.class, source)).a("]]")
            .toString();
    }

    /**
     * Returns a string representation of the entry key or entry value.
     *
     * @param value unwrappedKey or unwrappedValue
     * @param co    key or value
     * @return String presentation of the entry key or entry value depends on {@code isValue}.
     */
    public String toString(Object value, CacheObject co) {<FILL_FUNCTION_BODY>}

    /**
     * Produces auto-generated output of string presentation for given object (given the whole hierarchy).
     *
     * @param cls Declaration class of the object.
     * @param obj Object to get a string presentation for.
     * @return String presentation of the given object.
     */
    public static String toStringRecursive(Class cls, Object obj) {
        String result = null;

        if (cls != Object.class)
            result = S.toString(cls, obj, toStringRecursive(cls.getSuperclass(), obj));

        return result;
    }
}

        String str;
        if (sensitiveData == HIDE)
            return "";

        if (sensitiveData == HASH)
            if (value != null)
                return Integer.toString(value.hashCode());
            else
                return Integer.toString(co.hashCode());

        if (value instanceof String)
            str = (String)value;
        else if (value instanceof BinaryObject)
            str = value.toString();
        else if (value != null)
            str = toStringRecursive(value.getClass(), value);
        else if (co instanceof BinaryObject)
            str = co.toString();
        else
            str = null;

        if (str == null || str.isEmpty()) {

            try {
                CacheObjectValueContext ctx = null;
                try {
                    ctx = IgniteUtils.field(source, "cacheObjValCtx");
                }
                catch (Exception e) {
                    throw new IgniteException(e);
                }
                str = Base64.getEncoder().encodeToString(co.valueBytes(ctx));
            }
            catch (IgniteCheckedException e) {
                throw new IgniteException(e);
            }
        }

        if (sensitiveData == MD5)
            str = ProcessSensitiveDataUtils.md5(str);

        return str;
    
<methods>public void <init>(int, org.apache.ignite.internal.processors.cache.KeyCacheObject, org.apache.ignite.internal.processors.cache.CacheObject, org.apache.ignite.internal.processors.cache.GridCacheOperation, org.apache.ignite.internal.processors.cache.version.GridCacheVersion, org.apache.ignite.internal.processors.cache.version.GridCacheVersion, long, int, long, byte) ,public int cacheId() ,public long expireTime() ,public static byte flags(boolean) ,public static byte flags(boolean, boolean, boolean) ,public byte flags() ,public org.apache.ignite.internal.processors.cache.KeyCacheObject key() ,public org.apache.ignite.internal.processors.cache.version.GridCacheVersion nearXidVersion() ,public org.apache.ignite.internal.processors.cache.GridCacheOperation op() ,public long partitionCounter() ,public org.apache.ignite.internal.pagemem.wal.record.DataEntry partitionCounter(long) ,public int partitionId() ,public java.lang.String toString() ,public org.apache.ignite.internal.processors.cache.CacheObject value() ,public org.apache.ignite.internal.processors.cache.version.GridCacheVersion writeVersion() <variables>public static final byte EMPTY_FLAGS,public static final byte FROM_STORE_FLAG,public static final byte PRELOAD_FLAG,public static final byte PRIMARY_FLAG,protected int cacheId,protected long expireTime,protected byte flags,protected org.apache.ignite.internal.processors.cache.KeyCacheObject key,protected org.apache.ignite.internal.processors.cache.version.GridCacheVersion nearXidVer,protected org.apache.ignite.internal.processors.cache.GridCacheOperation op,protected long partCnt,protected int partId,protected org.apache.ignite.internal.processors.cache.CacheObject val,protected org.apache.ignite.internal.processors.cache.version.GridCacheVersion writeVer