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

org.babyfish.jimmer.ImmutableObjects Maven / Gradle / Ivy

There is a newer version: 0.9.17
Show newest version
package org.babyfish.jimmer;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.babyfish.jimmer.jackson.ImmutableModule;
import org.babyfish.jimmer.meta.*;
import org.babyfish.jimmer.runtime.DraftSpi;
import org.babyfish.jimmer.runtime.ImmutableSpi;
import org.babyfish.jimmer.runtime.Internal;
import org.jetbrains.annotations.Nullable;

import java.util.*;

public class ImmutableObjects {

    private static final ObjectMapper MAPPER;

    private ImmutableObjects() {}

    /**
     * Jimmer object is dynamic, none properties are mandatory.
     *
     * This method can ask whether a property of the object is specified.
     *
     * @param immutable Object instance
     * @param prop Property id
     * @return Whether the property of the object is specified.
     * @exception IllegalArgumentException The first argument is immutable object created by jimmer
     */
    public static boolean isLoaded(Object immutable, PropId prop) {
        if (immutable instanceof ImmutableSpi) {
            return ((ImmutableSpi) immutable).__isLoaded(prop);
        }
        throw new IllegalArgumentException("The first argument is immutable object created by jimmer");
    }

    /**
     * Jimmer object is dynamic, none properties are mandatory.
     *
     * This method can ask whether a property of the object is specified.
     *
     * @param immutable Object instance
     * @param prop Property name
     * @return Whether the property of the object is specified.
     * @exception IllegalArgumentException The first argument is immutable object created by jimmer
     */
    public static boolean isLoaded(Object immutable, String prop) {
        if (immutable instanceof ImmutableSpi) {
            return ((ImmutableSpi) immutable).__isLoaded(prop);
        }
        throw new IllegalArgumentException("The first argument is immutable object created by jimmer");
    }

    /**
     * Jimmer object is dynamic, none properties are mandatory.
     *
     * This method can ask whether a property of the object is specified.
     *
     * @param immutable Object instance
     * @param prop Property
     * @return Whether the property of the object is specified.
     * @exception IllegalArgumentException The first argument is immutable object created by jimmer
     */
    public static boolean isLoaded(Object immutable, ImmutableProp prop) {
        return isLoaded(immutable, prop.getId());
    }

    /**
     * Jimmer object is dynamic, none properties are mandatory.
     *
     * This method can ask whether a property of the object is specified.
     *
     * @param immutable Object instance
     * @param prop Property
     * @return Whether the property of the object is specified.
     * @exception IllegalArgumentException The first argument is immutable object created by jimmer
     */
    public static boolean isLoaded(Object immutable, TypedProp prop) {
        return isLoaded(immutable, prop.unwrap().getId());
    }

    @SuppressWarnings("unchecked")
    public static boolean isLoadedChain(Object immutable, PropId ... propIds) {
        ImmutableSpi spi = (ImmutableSpi) immutable;
        int parentCount = propIds.length - 1;
        if (parentCount == -1) {
            return true;
        }
        for (int i = 0; i < parentCount; i++) {
            if (spi == null) {
                return true;
            }
            PropId propId = propIds[i];
            if (!spi.__isLoaded(propId)) {
                return false;
            }
            Object value = spi.__get(propId);
            if (value instanceof ImmutableSpi) {
                spi = (ImmutableSpi) value;
            } else if (spi.__type().getProp(propId).isReferenceList(TargetLevel.ENTITY)) {
                List list = (List) value;
                PropId[] itemPropIds = Arrays.copyOfRange(propIds, i + 1, propIds.length);
                for (Object item : list) {
                    if (!isLoadedChain(item, itemPropIds)) {
                        return false;
                    }
                }
                return true;
            } else if (value != null) {
                throw new IllegalArgumentException(
                        "The properties[" +
                                i +
                                "] \"" +
                                spi.__type().getProp(propId) +
                                "\" is neither association nor embedded property"
                );
            }
        }
        return spi == null || spi.__isLoaded(propIds[parentCount]);
    }

    /**
     * Get the property value of immutable object,
     * if the property is not loaded, exception will be thrown.
     *
     * @param immutable Object instance
     * @param prop Property id
     * @return Whether the property of the object is specified.
     * @exception IllegalArgumentException There are two possibilities
     *      
    *
  • The first argument is immutable object created by jimmer
  • *
  • The second argument is not a valid property name of immutable object
  • *
* @exception UnloadedException The property is not loaded */ public static Object get(Object immutable, PropId prop) { if (immutable instanceof ImmutableSpi) { return ((ImmutableSpi) immutable).__get(prop); } throw new IllegalArgumentException("The first argument is immutable object created by jimmer"); } /** * Get the property value of immutable object, * if the property is not loaded, exception will be thrown. * * @param immutable Object instance * @param prop Property name * @return Whether the property of the object is specified. * @exception IllegalArgumentException There are two possibilities *
    *
  • The first argument is immutable object created by jimmer
  • *
  • The second argument is not a valid property name of immutable object
  • *
* @exception UnloadedException The property is not loaded */ public static Object get(Object immutable, String prop) { if (immutable instanceof ImmutableSpi) { return ((ImmutableSpi) immutable).__get(prop); } throw new IllegalArgumentException("The first argument is immutable object created by jimmer"); } /** * Get the property value of immutable object, * if the property is not loaded, exception will be thrown. * * @param immutable Object instance * @param prop Property * @return Whether the property of the object is specified. * @exception IllegalArgumentException There are two possibilities *
    *
  • The first argument is immutable object created by jimmer
  • *
  • The second argument is not a valid property name of immutable object
  • *
* @exception UnloadedException The property is not loaded */ public static Object get(Object immutable, ImmutableProp prop) { return get(immutable, prop.getId()); } /** * Get the property value of immutable object, * if the property is not loaded, exception will be thrown. * * @param The entity type * @param The property type * * @param immutable Object instance * @param prop Property * @return Whether the property of the object is specified. * @exception IllegalArgumentException There are two possibilities *
    *
  • The first argument is immutable object created by jimmer
  • *
  • The second argument is not a valid property name of immutable object
  • *
* @exception UnloadedException The property is not loaded */ @SuppressWarnings("unchecked") public static X get(T immutable, TypedProp prop) { return (X)get(immutable, prop.unwrap().getId()); } public static boolean isIdOnly(Object immutable) { if (immutable == null) { return false; } if (immutable instanceof ImmutableSpi) { ImmutableSpi spi = (ImmutableSpi) immutable; ImmutableType type = spi.__type(); ImmutableProp idProp = type.getIdProp(); if (idProp == null) { throw new IllegalArgumentException("The object type \"" + type + "\" does not have id property"); } for (ImmutableProp prop : type.getProps().values()) { if (prop.isId()) { if (!spi.__isLoaded(prop.getId())) { throw new IllegalArgumentException("The id of " + spi + " is unloaded"); } } else if (spi.__isLoaded(prop.getId())) { return false; } } return true; } throw new IllegalArgumentException("The first argument is immutable object created by jimmer"); } @Nullable public static T makeIdOnly(Class type, @Nullable Object id) { return makeIdOnly(ImmutableType.get(type), id); } @Nullable @SuppressWarnings("unchecked") public static T makeIdOnly(ImmutableType type, @Nullable Object id) { ImmutableProp idProp = type.getIdProp(); if (idProp == null) { throw new IllegalArgumentException("No id property in \"" + type + "\""); } if (id == null) { return null; } return (T) Internal.produce(type, null, draft -> { DraftSpi targetDraft = (DraftSpi) draft; targetDraft.__set(idProp.getId(), id); }); } public static boolean isLonely(Object immutable) { if (immutable instanceof ImmutableSpi) { ImmutableSpi spi = (ImmutableSpi) immutable; ImmutableType type = spi.__type(); for (ImmutableProp prop : type.getProps().values()) { if (prop.isAssociation(TargetLevel.ENTITY) && spi.__isLoaded(prop.getId())) { if (prop.isColumnDefinition()) { ImmutableSpi target = (ImmutableSpi) spi.__get(prop.getId()); if (target != null && !isIdOnly(target)) { return false; } } else { return false; } } } } throw new IllegalArgumentException("The first argument is immutable object created by jimmer"); } @SuppressWarnings("unchecked") public static T toLonely(T immutable) { if (immutable == null) { return null; } ImmutableSpi spi = (ImmutableSpi) immutable; ImmutableType type = spi.__type(); return (T)Internal.produce(type, immutable, draft -> { for (ImmutableProp prop : type.getProps().values()) { PropId propId = prop.getId(); if (prop.isAssociation(TargetLevel.ENTITY) && spi.__isLoaded(propId)) { if (prop.isColumnDefinition()) { ImmutableSpi target = (ImmutableSpi) spi.__get(propId); if (target != null) { ImmutableType targetType = prop.getTargetType(); PropId targetIdPropId = targetType.getIdProp().getId(); if (!target.__isLoaded(targetIdPropId)) { ((DraftSpi) draft).__unload(propId); } else if (!isIdOnly(target)) { Object targetId = target.__get(targetIdPropId); ((DraftSpi) draft).__set(propId, makeIdOnly(targetType, targetId)); } } } else { ((DraftSpi) draft).__unload(propId); } } } }); } public static Collection toIdOnly(Iterable immutables) { if (immutables instanceof Set) { return toIdOnly((Set) immutables); } List idOnlyList = immutables instanceof Collection ? new ArrayList<>(((Collection)immutables).size()) : new ArrayList<>(); for (T immutable : immutables) { idOnlyList.add(toIdOnly(immutable)); } return idOnlyList; } public static Set toIdOnly(Set immutables) { Set idOnlySet = new LinkedHashSet<>((immutables.size() * 4 + 2) / 3); for (T immutable : immutables) { idOnlySet.add(toIdOnly(immutable)); } return idOnlySet; } public static List toIdOnly(List immutables) { List idOnlyList = new ArrayList<>(immutables.size()); for (T immutable : immutables) { idOnlyList.add(toIdOnly(immutable)); } return idOnlyList; } public static T toIdOnly(T immutable) { if (immutable == null) { return null; } ImmutableSpi spi = (ImmutableSpi) immutable; ImmutableType type = spi.__type(); ImmutableProp idProp = type.getIdProp(); if (idProp == null) { throw new IllegalArgumentException( "Cannot convert \"" + type + "\" to id only object because it is not entity" ); } if (!spi.__isLoaded(idProp.getId())) { throw new IllegalArgumentException( "Cannot convert \"" + type + "\" to id only object because its id property \"" + type.getIdProp() + "\"" ); } return makeIdOnly(type, spi.__get(idProp.getId())); } /** * Convert an object to a JSON string. * If the object is jimmer immutable object, unspecified properties can be automatically ignored. * * @param immutable Any object * @return JSON string */ public static String toString(Object immutable) { try { return MAPPER.writeValueAsString(immutable); } catch (JsonProcessingException ex) { throw new RuntimeException(ex); } } /** * Convert a JSON string to an object. * * @param type Object type, can be interface type. * @return Deserialized object */ public static I fromString(Class type, String json) throws JsonProcessingException { return MAPPER.readValue(json, type); } public static I fromString(Class type, String json, ObjectMapper mapper) throws JsonProcessingException { return mapper.readValue(json, type); } @SuppressWarnings("unchecked") @SafeVarargs public static I merge(I ... parts) { List spis = new ArrayList<>(parts.length); for (I part : parts) { if (part == null) { continue; } if (!(part instanceof ImmutableSpi)) { throw new IllegalArgumentException("Each element of `parts` must be immutable object"); } ImmutableSpi spi = (ImmutableSpi) part; if (!spis.isEmpty()) { if (spis.get(0).__type() != spi.__type()) { throw new IllegalArgumentException( "All element of `parts` must belong to same type, but both \"" + spis.get(0).__type() + "\" and \"" + spi.__type() + "\" are found" ); } } spis.add(spi); } if (spis.size() == 1) { return (I) spis.get(0); } return (I) Internal.produce(spis.get(0).__type(), null, draft -> { for (ImmutableSpi spi : spis) { merge((DraftSpi) draft, spi); } }); } private static void merge(DraftSpi target, ImmutableSpi source) { for (ImmutableProp prop : source.__type().getProps().values()) { PropId propId = prop.getId(); if (source.__isLoaded(propId)) { target.__set(propId, source.__get(propId)); } } } @SuppressWarnings("unchecked") public static I deepClone(I immutable) { if (immutable == null) { return null; } ImmutableType immutableType = ImmutableType.get(immutable.getClass()); return (I) deepClone(immutableType, immutable); } @SuppressWarnings("unchecked") private static Object deepClone(ImmutableType type, Object immutable) { ImmutableSpi from = (ImmutableSpi) immutable; return Internal.produce(type, null, draft -> { DraftSpi to = (DraftSpi) draft; for (ImmutableProp prop : type.getProps().values()) { PropId propId = prop.getId(); if (prop.isView() || !prop.isMutable() || !from.__isLoaded(propId)) { continue; } ImmutableType targetType = prop.getTargetType(); if (prop.isReferenceList(TargetLevel.OBJECT)) { List targets = (List) from.__get(propId); List clonedTargets = new ArrayList<>(targets.size()); for (Object target : targets) { clonedTargets.add(deepClone(targetType, target)); } to.__set(propId, clonedTargets); } else if (prop.isReference(TargetLevel.OBJECT)) { Object target = from.__get(propId); if (target != null) { to.__set(propId, deepClone(targetType, target)); } } else { to.__set(propId, from.__get(propId)); } } }); } public static boolean isLogicalDeleted(Object o) { if (!(o instanceof ImmutableSpi)) { return false; } ImmutableSpi spi = (ImmutableSpi) o; LogicalDeletedInfo info = spi.__type().getLogicalDeletedInfo(); if (info == null) { return false; } PropId propId = info.getProp().getId(); if (!spi.__isLoaded(propId)) { return false; } return info.isDeleted(spi.__get(propId)); } static { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.registerModule(new ImmutableModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); MAPPER = mapper; } }