All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aerospike.mapper.tools.mappers.ObjectReferenceMapper Maven / Gradle / Ivy

package com.aerospike.mapper.tools.mappers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.aerospike.client.AerospikeException;
import com.aerospike.client.Value;
import com.aerospike.client.util.Crypto;
import com.aerospike.mapper.annotations.AerospikeReference.ReferenceType;
import com.aerospike.mapper.tools.*;
import com.aerospike.mapper.tools.DeferredObjectLoader.DeferredObject;

public class ObjectReferenceMapper extends ObjectMapper {

    // Package visibility
    private final ClassCacheEntry referencedClass;
    private final IBaseAeroMapper mapper;
    private final boolean lazy;
    private final boolean allowBatch;
    private final ReferenceType type;

    public ObjectReferenceMapper(ClassCacheEntry entry, boolean lazy, boolean allowBatch,
                                 ReferenceType type, IBaseAeroMapper mapper) {
        this.referencedClass = entry;
        this.mapper = mapper;
        this.lazy = lazy;
        this.type = type;
        this.allowBatch = allowBatch;

        if (ReferenceType.DIGEST.equals(this.type) && this.lazy) {
            throw new AerospikeException("An object reference to a " + entry.getClass().getSimpleName()
                    + " cannot be both lazy and map to a digest");
        }
    }

    @Override
    public Object toAerospikeFormat(Object value) {
        return toAerospikeFormat(value, false, false);
    }

    @Override
    public Object toAerospikeFormat(Object value, boolean isUnknownType, boolean isSubclassOfKnownType) {
        if (value == null) {
            return null;
        }
        // In this case we want to store a reference to the object.
        ClassCacheEntry classToUse;
        if (value.getClass().equals(referencedClass.getUnderlyingClass())) {
            classToUse = referencedClass;
        } else {
            classToUse = ClassCache.getInstance().loadClass(value.getClass(), mapper);
            isSubclassOfKnownType = true;
        }
        Object key = classToUse.getKey(value);
        if (ReferenceType.DIGEST.equals(type)) {
            key = Crypto.computeDigest(classToUse.getSetName(), Value.get(key));
        }
        if (isSubclassOfKnownType || isUnknownType) {
            // Need to put the class name in the key so we can recreate the class
            List keyParts = new ArrayList<>();
            keyParts.add(key);
            if (isUnknownType) {
                // Must put in an identifier to mark this as an unknown type
                keyParts.add(ClassCacheEntry.TYPE_PREFIX + classToUse.getShortenedClassName());
            } else {
                keyParts.add(classToUse.getShortenedClassName());
            }
            return keyParts;
        }
        return key;
    }

    @Override
    public Object fromAerospikeFormat(Object value) {
        // The object should be the primary key of the referencing object
        if (value == null) {
            return null;
        }
        ClassCacheEntry classToUse = referencedClass;

        Object key;
        if (value instanceof List) {
            List list = (List) value;
            key = list.get(0);
            String typeName = (String) list.get(1);
            if (typeName.startsWith(ClassCacheEntry.TYPE_PREFIX)) {
                typeName = typeName.substring(ClassCacheEntry.TYPE_PREFIX.length());
            }
            classToUse = ClassCache.getInstance().getCacheEntryFromStoredName(typeName);
        } else {
            key = value;
        }

        if (this.lazy) {
            Map map = new HashMap<>();
            Object instance = classToUse.constructAndHydrate(map);
            classToUse.setKey(instance, key);
            return instance;
        } else if (allowBatch) {
            return new DeferredObject(key, classToUse.getUnderlyingClass(), ReferenceType.DIGEST.equals(type));
        } else if (ReferenceType.DIGEST.equals(type)) {
            return mapper.asMapper().readFromDigest(classToUse.getUnderlyingClass(), (byte[]) key, false);
        } else {
            return mapper.asMapper().read(classToUse.getUnderlyingClass(), key, false);
        }
    }
}