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

org.dbflute.dbmeta.accessory.EntityDerivedMap Maven / Gradle / Ivy

There is a newer version: 1.2.8
Show newest version
/*
 * Copyright 2014-2019 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.dbflute.dbmeta.accessory;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.dbflute.Entity;
import org.dbflute.exception.SpecifyDerivedReferrerInvalidAliasNameException;
import org.dbflute.exception.SpecifyDerivedReferrerPropertyValueNotFoundException;
import org.dbflute.exception.SpecifyDerivedReferrerUnknownAliasNameException;
import org.dbflute.exception.SpecifyDerivedReferrerUnmatchedPropertyTypeException;
import org.dbflute.helper.message.ExceptionMessageBuilder;
import org.dbflute.optional.OptionalScalar;

/**
 * The derived map of entity. (basically for Framework)
 * @author jflute
 * @since 1.1.0 (2014/10/29 Monday)
 */
public class EntityDerivedMap implements Serializable, Cloneable {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    /** The serial version UID for object serialization. (Default) */
    private static final long serialVersionUID = 1L;

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    /** The map of derived value. map:{alias-name = value} (NullAllowed: lazy-loaded) */
    protected Map _derivedMap;

    // ===================================================================================
    //                                                                            Register
    //                                                                            ========
    /**
     * Register the derived value to the map.
     * @param aliasName The alias name of derived-referrer. (NotNull)
     * @param selectedValue The derived value selected from database. (NullAllowed: when null selected)
     */
    public void registerDerivedValue(String aliasName, Object selectedValue) {
        getDerivedMap().put(aliasName, selectedValue);
    }

    // ===================================================================================
    //                                                                               Find
    //                                                                              ======
    /**
     * Find the derived value in the map.
     * @param  The type of derived value.
     * @param entity The entity that has the derived value, basically for logging. (NotNull)
     * @param aliasName The alias name of derived-referrer. (NotNull)
     * @param propertyType The type of the derived property, should match as rule. (NotNull)
     * @return The optional scalar for derived value found in the map. (NotNull, EmptyAllowed: when null selected)
     * @throws SpecifyDerivedReferrerInvalidAliasNameException When the alias name does not start with '$'.
     * @throws SpecifyDerivedReferrerUnknownAliasNameException When the alias name is unknown, no derived.
     * @throws SpecifyDerivedReferrerUnmatchedPropertyTypeException When the property type is unmatched with actual type.
     */
    public  OptionalScalar findDerivedValue(Entity entity, String aliasName, Class propertyType) {
        if (aliasName == null) {
            throw new IllegalArgumentException("The argument 'aliasName' should not be null.");
        }
        if (propertyType == null) {
            throw new IllegalArgumentException("The argument 'propertyType' should not be null.");
        }
        if (aliasName.trim().length() == 0) {
            throw new IllegalArgumentException("The argument 'aliasName' should not be empty: [" + aliasName + "]");
        }
        if (!aliasName.startsWith(DerivedMappable.MAPPING_ALIAS_PREFIX)) {
            throwInvalidDerivedAliasNameException(entity, aliasName);
        }
        final Map derivedMap = getDerivedMap(); // lazy-loaded if nothing
        if (!derivedMap.containsKey(aliasName)) {
            throwUnknownDerivedAliasNameException(entity, aliasName, derivedMap);
        }
        try {
            final VALUE found = propertyType.cast(derivedMap.get(aliasName)); // also checking type
            final String tableDbName = entity.asTableDbName(); // not to have reference to entity in optional
            return OptionalScalar.ofNullable(found, () -> {
                throwDerivedPropertyValueNotFoundException(tableDbName, aliasName);
            }); // null allowed
        } catch (ClassCastException e) {
            throwUnmatchDerivedPropertyTypeException(entity, aliasName, propertyType, e);
            return null; // unreachable
        }
    }

    // ===================================================================================
    //                                                                              Others
    //                                                                              ======
    /**
     * Is the derived map empty?
     * @return The determination, true or false.
     */
    public boolean isEmpty() {
        return getDerivedMap().isEmpty();
    }

    /**
     * Clear the derived map.
     */
    public void clear() {
        getDerivedMap().clear();
    }

    /**
     * Remove the derived value from the map.
     * @param aliasName The alias name of derived-referrer. (NotNull)
     */
    public void remove(String aliasName) {
        getDerivedMap().remove(aliasName);
    }

    // ===================================================================================
    //                                                                         Derived Map
    //                                                                         ===========
    protected Map getDerivedMap() {
        if (_derivedMap == null) {
            _derivedMap = newDerivedMap();
        }
        return _derivedMap;
    }

    protected HashMap newDerivedMap() {
        return new HashMap();
    }

    // ===================================================================================
    //                                                                    Exception Helper
    //                                                                    ================
    protected void throwInvalidDerivedAliasNameException(Entity entity, String aliasName) {
        final ExceptionMessageBuilder br = new ExceptionMessageBuilder();
        br.addNotice("Illegal alias name (not start with '$') for the derived property.");
        br.addItem("Advice");
        br.addElement("Make sure your alias name to find the derived value.");
        br.addElement("You should specify the name that starts with '$'.");
        br.addElement("For example:");
        br.addElement("  (x):");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, \"$HIGHEST_PURCHASE_PRICE\");");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    ... = member.derived(\"HIGHEST_PURCHASE_PRICE\", Integer.class); // *NG");
        br.addElement("  (o):");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, \"$HIGHEST_PURCHASE_PRICE\");");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    ... = member.derived(\"$HIGHEST_PURCHASE_PRICE\", Integer.class); // OK");
        br.addItem("Table");
        buildExceptionTableInfo(br, entity);
        br.addItem("Illegal Alias");
        br.addElement(aliasName);
        final String msg = br.buildExceptionMessage();
        throw new SpecifyDerivedReferrerInvalidAliasNameException(msg);
    }

    protected void throwUnknownDerivedAliasNameException(Entity entity, String aliasName, Map derivedMap) {
        final ExceptionMessageBuilder br = new ExceptionMessageBuilder();
        br.addNotice("Not found the alias name in the derived map");
        br.addItem("Advice");
        br.addElement("Make sure your alias name to find the derived value.");
        br.addElement("You should specify the name specified as DerivedReferrer.");
        br.addElement("For example:");
        br.addElement("  (x):");
        br.addElement("    String highestAlias = \"$HIGHEST_PURCHASE_PRICE\"");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, highestAlias);");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    ... = member.derived(\"$BIG_SHOT\", Integer.class); // *NG");
        br.addElement("  (o):");
        br.addElement("    String highestAlias = \"$HIGHEST_PURCHASE_PRICE\"");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, highestAlias);");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    ... = member.derived(highestAlias, Integer.class); // OK");
        br.addItem("Table");
        buildExceptionTableInfo(br, entity);
        br.addItem("Unknown Alias");
        br.addElement(aliasName);
        buildExceptionExistingDerivedMapInfo(br, derivedMap);
        final String msg = br.buildExceptionMessage();
        throw new SpecifyDerivedReferrerUnknownAliasNameException(msg);
    }

    protected void throwUnmatchDerivedPropertyTypeException(Entity entity, String aliasName, Class propertyType,
            ClassCastException cause) {
        final ExceptionMessageBuilder br = new ExceptionMessageBuilder();
        br.addNotice("Illegal property type for the derived property.");
        br.addItem("Advice");
        br.addElement("Make sure your property type to find the derived value.");
        br.addElement("You should specify the matched type, it's rule is following:");
        br.addElement("  count()      : Integer");
        br.addElement("  max(), min() : (same as property type of the column)");
        br.addElement("  sum(), avg() : BigDecimal");
        br.addElement("");
        br.addElement("For example:");
        br.addElement("  (x):");
        br.addElement("    String highestAlias = \"$HIGHEST_PURCHASE_PRICE\"");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, highestAlias);");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    ... = member.derived(highestAlias, LocalDate.class); // *NG");
        br.addElement("  (o):");
        br.addElement("    String highestAlias = \"$HIGHEST_PURCHASE_PRICE\"");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, highestAlias);");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    ... = member.derived(highestAlias, Integer.class); // OK");
        buildExceptionTableInfo(br, entity);
        br.addItem("NotFound Alias");
        br.addElement(aliasName);
        br.addItem("Specified PropertyType");
        br.addElement(propertyType);
        buildExceptionExistingDerivedMapInfo(br, getDerivedMap());
        final String msg = br.buildExceptionMessage();
        throw new SpecifyDerivedReferrerUnmatchedPropertyTypeException(msg, cause);
    }

    protected void buildExceptionTableInfo(ExceptionMessageBuilder br, Entity entity) {
        br.addItem("Table");
        br.addElement(entity.asTableDbName());
        try {
            br.addElement(entity.asDBMeta().extractPrimaryKeyMap(entity));
        } catch (RuntimeException continued) { // just in case
            br.addElement("*Failed to get PK info:");
            br.addElement(continued.getMessage());
        }
    }

    protected void buildExceptionExistingDerivedMapInfo(ExceptionMessageBuilder br, Map derivedMap) {
        br.addItem("Existing DerivedMap");
        for (Entry entry : derivedMap.entrySet()) {
            final Object value = entry.getValue();
            br.addElement(entry.getKey() + " = " + (value != null ? value.getClass() : null));
        }
    }

    protected void throwDerivedPropertyValueNotFoundException(String tableDbName, String aliasName) { // embedded in optional
        final ExceptionMessageBuilder br = new ExceptionMessageBuilder();
        br.addNotice("Not found the derived property value.");
        br.addItem("Advice");
        br.addElement("Please confirm the existence your property value.");
        br.addElement("Especially e.g. max(), sum(), ... might return null if no referrer data");
        br.addElement("For example:");
        br.addElement("  (x):");
        br.addElement("    String highestAlias = \"$HIGHEST_PURCHASE_PRICE\"");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, highestAlias);");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    member.derived(highestAlias, Integer.class).alwaysPresent(...); // *NG");
        br.addElement("  (o):");
        br.addElement("    String highestAlias = \"$HIGHEST_PURCHASE_PRICE\"");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, highestAlias);");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    member.derived(highestAlias, Integer.class).ifPresent(...); // OK");
        br.addElement("  (o):");
        br.addElement("    String highestAlias = \"$HIGHEST_PURCHASE_PRICE\"");
        br.addElement("    cb.specify().derivedPurchaseList().max(purchaseCB -> {");
        br.addElement("        purchaseCB.specify().columnPurchasePrice();");
        br.addElement("    }, highestAlias, op -> op.coalesce(0)); // *point");
        br.addElement("    ...");
        br.addElement("    Member member = ...");
        br.addElement("    member.derived(highestAlias, Integer.class).alwaysPresent(...); // OK");
        br.addItem("Table");
        br.addElement(tableDbName);
        br.addItem("DerivedProperty (Alias)");
        br.addElement(aliasName);
        // embedded in optional so no reference to derived map
        //buildExceptionExistingDerivedMapInfo(br, getDerivedMap());
        final String msg = br.buildExceptionMessage();
        throw new SpecifyDerivedReferrerPropertyValueNotFoundException(msg);
    }

    // ===================================================================================
    //                                                                      Basic Override
    //                                                                      ==============
    @Override
    public String toString() {
        return "derivedMap:" + _derivedMap;
    }

    @Override
    public EntityDerivedMap clone() { // almost deep copy (value is shallow copy, not always immutable)
        try {
            final EntityDerivedMap cloned = (EntityDerivedMap) super.clone();
            if (_derivedMap != null) {
                final Map copied = newDerivedMap();
                copied.putAll(_derivedMap);
                cloned._derivedMap = copied;
            }
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new IllegalStateException("Failed to clone the derived map: " + toString(), e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy