class IndexRowImpl implements IndexRow {
    /** Object that contains info about original IgniteCache row. */
    private final CacheDataRow cacheRow;

    /** Cache for index row keys. To avoid hit underlying cache for every comparation. */
    private IndexKey[] keyCache;

    /** Schema of an index. */
    private final InlineIndexRowHandler rowHnd;

    /** Constructor. */
    public IndexRowImpl(InlineIndexRowHandler rowHnd, CacheDataRow row) {
        this(rowHnd, row, null);
    }

    /**
     * Constructor with prefilling of keys cache.
     */
    public IndexRowImpl(InlineIndexRowHandler rowHnd, CacheDataRow row, IndexKey[] keys) {
        this.rowHnd = rowHnd;
        cacheRow = row;
        keyCache = keys;
    }

    /**
     * @return Indexed value.
     */
    public CacheObject value() {
        return cacheRow.value();
    }

    /** {@inheritDoc} */
    @Override public IndexKey key(int idx) {<FILL_FUNCTION_BODY>}

    /** {@inheritDoc} */
    @Override public int keysCount() {
        return rowHnd.indexKeyDefinitions().size();
    }

    /** {@inheritDoc} */
    @Override public long link() {
        return cacheRow.link();
    }

    /** {@inheritDoc} */
    @Override public InlineIndexRowHandler rowHandler() {
        return rowHnd;
    }

    /** {@inheritDoc} */
    @Override public CacheDataRow cacheDataRow() {
        return cacheRow;
    }

    /**
     * @return Cache ID or {@code 0} if cache ID is not defined.
     */
    public int cacheId() {
        return cacheDataRow().cacheId();
    }

    /** Initialize a cache for index keys. Useful for inserting rows as there are a lot of comparisons. */
    public void prepareCache() {
        keyCache = new IndexKey[rowHnd.indexKeyDefinitions().size()];
    }

    /** {@inheritDoc} */
    @Override public String toString() {
        SB sb = new SB("Row@");

        sb.a(Integer.toHexString(System.identityHashCode(this)));

        Object v = rowHnd.cacheKey(cacheRow);

        sb.a("[ key: ").a(v == null ? "nil" : v.toString());

        v = rowHnd.cacheValue(cacheRow);
        sb.a(", val: ").a(v == null ? "nil" : (S.includeSensitive() ? v.toString() :
            "Data hidden due to " + IGNITE_TO_STRING_INCLUDE_SENSITIVE + " flag."));

        sb.a(" ][ ");

        if (v != null) {
            for (int i = 0, cnt = rowHnd.indexKeyDefinitions().size(); i < cnt; i++) {
                if (i != 0)
                    sb.a(", ");

                try {
                    v = key(i);

                    sb.a(v == null ? "nil" : (S.includeSensitive() ? v.toString() : "data hidden"));
                }
                catch (Exception e) {
                    sb.a("<value skipped on error: " + e.getMessage() + '>');
                }
            }
        }

        sb.a(" ]");

        return sb.toString();
    }

    /** {@inheritDoc} */
    @Override public boolean indexPlainRow() {
        return false;
    }
}

        if (keyCache != null && keyCache[idx] != null)
            return keyCache[idx];

        IndexKey key = rowHnd.indexKey(idx, cacheRow);

        if (keyCache != null)
            keyCache[idx] = key;

        return key;
    
<no_super_class>