Package com.force.sdk.jpa

Source Code of com.force.sdk.jpa.ForceInsertFieldManager

/**
* Copyright (c) 2011, salesforce.com, inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
*    Redistributions of source code must retain the above copyright notice, this list of conditions and the
*    following disclaimer.
*
*    Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
*    the following disclaimer in the documentation and/or other materials provided with the distribution.
*
*    Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
*    promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

package com.force.sdk.jpa;

import com.force.sdk.jpa.model.PicklistValueEnum;
import com.force.sdk.jpa.table.ColumnImpl;
import com.force.sdk.jpa.table.TableImpl;
import com.sforce.soap.partner.sobject.SObject;
import com.sforce.ws.types.Time;
import org.datanucleus.ObjectManager;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.FieldPersistenceModifier;
import org.datanucleus.state.ObjectProviderImpl;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.fieldmanager.AbstractFieldManager;

import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;

/**
*
* Field manager for updating records.  This class takes updated fields and stores them on the
* SObject which will eventually be pushed to Force.com via a SOQL update (or create or delete)
*
* @author Fiaz Hossain
*/
public class ForceInsertFieldManager extends AbstractFieldManager {

    private static final Random PART_B = new Random(System.nanoTime());

    private final ObjectProvider objectProvider;
    private final ForceStoreManager storeManager;
    private final SObject sobject;
    private final TableImpl table;
    private final int pkIndexToSkip;
    private final int versionIndexToSkip;
    private boolean dirty;
   
    /**
     * Creates a manager that will upsert the specified field values of a particular entity.
     *
     * @param objectProvider the object provider for this entity
     * @param storeManager the store manager
     * @param pkValue the id object (string for Force.com entities) for this entity, <code>null</code> if this is an insert
     */
    public ForceInsertFieldManager(ObjectProvider objectProvider, ForceStoreManager storeManager, Object pkValue) {
        this.objectProvider = objectProvider;
        this.storeManager = storeManager;
        this.sobject = new SObject();
        AbstractClassMetaData acmd = objectProvider.getClassMetaData();
        this.table = storeManager.getTable(acmd);
        if (this.table == null) {
            throw new NucleusUserException("Entity does not exist in Force.com datastore: " + acmd.getEntityName());
        }
        sobject.setType(this.table.getTableName().getForceApiName());
        if (pkValue != null) {
            sobject.addField(table.getPKFieldName(acmd), pkValue);
        } else {
            // If PK value is null, this is an insert and we consider this object to be dirty all the time
            dirty = true;
        }
        // During insert this field index should not be added as it is the "id" field which should be null.
        // During update it has already been added above so it needs to be skipped too to avoid duplicate fields in statement
        pkIndexToSkip = acmd.getPKMemberPositions()[0];
        if (acmd.getVersionMetaData() != null && pkValue != null) {
            versionIndexToSkip = acmd.getAbsolutePositionOfMember(acmd.getVersionMetaData().getFieldName());
            Object version = objectProvider.provideField(versionIndexToSkip);
            if (version == null && storeManager.isEnableOptimisticTransactions()) {
                throw new NucleusUserException("Version field must be set to non-null value, field: "
                                                + acmd.getVersionMetaData().getFieldName() + " object id:" + sobject.getId());
            }
            objectProvider.setVersion(version);
        } else {
            versionIndexToSkip = -1;
        }
    }

    /**
     * Retrieves the Force.com object (SObject) for this {@code ForceInsertFieldManager}.
     *
     * @param appendExternalId flag indicating if an external id should be added to
     *                         the Force.com object (SObject) if none exists
     * @return the Force.com object (SObject) for this {@code ForceInsertFieldManager}
     */
    public SObject getSObject(boolean appendExternalId) {
        if (appendExternalId) {
            if (table.getExternalIdColumn() != null) {
                String name = table.getExternalIdColumn().getFieldName();
                if (sobject.getField(name) == null) {
                    sobject.setField(name, "" + System.nanoTime() + PART_B.nextInt());
                }
            }
        }
        return sobject;
    }
   
    /**
     * The entity is considered dirty if fields have been updated (meaning an update is necessary).
     * Entities with no ids, meaning they are for insert, are always marked as dirty.
     * @return {@code true} if the entity is considered dirty and needs to be upserted
     */
    public boolean isDirty() {
        return dirty;
    }
   
    /**
     * All store methods delegate to this method for proper transformation on the value so it
     * can be stored in the sObject and upserted properly by the API.
     *
     * @param fieldNumber the index of the field to update
     * @param value the value that should be stored in the field
     */
    private void storeField(int fieldNumber, Object value) {
        if (value == null || fieldNumber == pkIndexToSkip || fieldNumber == versionIndexToSkip) return;
        AbstractMemberMetaData ammd =
            objectProvider.getClassMetaData().getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
        if (ammd.getEmbeddedMetaData() != null) {
            // we need to add all the embedded fields
            storeEmbeddedFields(ammd, value);
            return;
        }
        ColumnImpl column = table.getColumnFor(objectProvider.getClassMetaData(), ammd);
        if (column == null || column.getField() == null || !column.getField().isCreateable()) return;
        Object actualValue = value;
        String actualFieldName = column.getFieldName();
        try {
            switch (column.getType()) {
            case datetime:
                if (value instanceof Date) {
                    ColumnMetaData[] cmds = ammd.getColumnMetaData();
                    Calendar cal = Calendar.getInstance();
                    cal.setTime((Date) value);
                    if (cmds != null && cmds.length > 0 && "TIME".equals(cmds[0].getJdbcType())) {
                        actualValue = new Time(cal);
                    } else {
                        actualValue = cal;
                    }
                } else if (value instanceof Time) {
                    actualValue = value;
                }
                break;
            case currency:
                actualValue = value.toString();
                break;
            case reference:
                AbstractClassMetaData acmd =
                    storeManager.getMetaDataManager().getMetaDataForClass(ammd.getType(),
                                                        objectProvider.getExecutionContext().getClassLoaderResolver());
                if (acmd != null) {
                    actualValue = PersistenceUtils.getMemberValue(acmd, acmd.getPKMemberPositions()[0], value);
                    if (actualValue == null) {
                        ObjectManager om = ((ObjectProviderImpl) objectProvider).getStateManager().getObjectManager();
                        if (((ForceObjectManagerImpl) om).isInAllOrNothingMode()) {
                            /**
                             * This is instance of AllOrNothing transaction
                             * Since the parent object has not been saved to the db yet
                             * we do not have an id yet so we link objects by extId
                             */
                            SObject parentRef = new SObject();
                            TableImpl parent = storeManager.getTable(acmd);
                            if (parent.getExternalIdColumn() == null) {
                                throw new NucleusUserException("EntityManager in persistence.xml has 'force.AllOrNothing'"
                                                               + " set to true. In this mode all top parent Entities must"
                                                               + " have an externalId field. Offending entity: "
                                                               + parent.getTableName().getName());
                            }
                            parentRef.setType(parent.getTableName().getForceApiName());
                            SObject parentSObject = ((ForceObjectManagerImpl) om).getParentSObject(value);
                            parentRef.setField(parent.getExternalIdColumn().getFieldName(),
                                                parentSObject.getField(parent.getExternalIdColumn().getFieldName()));
                            actualValue = parentRef;
                            actualFieldName = column.getForceApiRelationshipName();
                        } else {
                            throw new NucleusUserException("Child entity cannot be saved before parent entity.");
                        }
                    }
                } else {
                    if (ammd.getCollection() != null || ammd.getMap() != null) {
                        /**
                         * There is no data to set on the parent as the relationship is only maintained from the child's end
                         */
                        return;
                    }
                }
                break;
            case multipicklist:
                if (value.getClass().isArray()) {
                    if (ammd.getType().getComponentType().isEnum()) {
                        Enum<?>[] enums = (Enum<?>[]) value;
                        boolean isOrdinal = PersistenceUtils.isOrdinalEnum(ammd);
                        boolean isPicklistValue = PicklistValueEnum[].class.isAssignableFrom(enums.getClass());
                       
                        StringBuilder sb = new StringBuilder(enums.length * 20);
                        for (Enum<?> e : enums) {
                            if (sb.length() > 0) sb.append(";");
                            if (isOrdinal) {
                                sb.append(e.ordinal());
                            } else if (isPicklistValue) {
                                sb.append(((PicklistValueEnum) e).value());
                            } else {
                                sb.append(e.name());
                            }
                        }
                        actualValue = sb.toString();
                    } else {
                        // else treat it like a non-strict picklist value
                        String[] values = (String[]) value;
                        StringBuilder sb = new StringBuilder(values.length * 20);
                        for (String v : values) {
                            if (sb.length() > 0) sb.append(";");
                            sb.append(v);
                        }
                        actualValue = sb.toString();
                    }
                    break;
                }
                // else treat it like a single picklist value
            case picklist:
                if (value.getClass().isEnum()) {
                    Enum e = (Enum) value;
                    if (PersistenceUtils.isOrdinalEnum(ammd)) {
                        actualValue = "" + e.ordinal();
                    } else if (e instanceof PicklistValueEnum) {
                        actualValue = ((PicklistValueEnum) e).value();
                    } else {
                        actualValue = e.name();
                    }
                }
                break;
            case url:
                actualValue = value.toString();
                break;
            case base64:
                if (ammd.getType() == byte[].class || ammd.getType() == Byte[].class) {
                    /**
                     * Odd that I did not have to encode this, yet when reading I have to decode it.
                     */
                    //actualValue = Base64.encode((byte[]) value);
                    actualValue = value;
                } else {
                    throw new NucleusUserException("Bad datatype for base64 encoding: " + ammd.getTypeName());
                }
                break;
            default:
            }
        } catch (InvocationTargetException x) {
            throw new NucleusException(x.getMessage(), x);
        } catch (IllegalAccessException x) {
            throw new NucleusException(x.getMessage(), x);
        }
        sobject.addField(actualFieldName, actualValue);
        dirty = true;
    }
   
    private void storeEmbeddedFields(AbstractMemberMetaData ammd, Object value) {
        AbstractClassMetaData cmd = objectProvider.getClassMetaData();
        for (AbstractMemberMetaData eammd : ammd.getEmbeddedMetaData().getMemberMetaData()) {
            if (eammd.getPersistenceModifier() != FieldPersistenceModifier.PERSISTENT) continue;
            try {
                Object newValue = PersistenceUtils.getMemberValue(eammd.getMemberRepresented(), value);
                if (eammd.getEmbeddedMetaData() != null) {
                    storeEmbeddedFields(eammd, newValue);
                } else {
                        ColumnImpl column = table.getColumnFor(cmd, eammd);
                        if (column != null) {
                            sobject.addField(column.getFieldName(), newValue);
                            dirty = true;
                        }
                }
            } catch (Exception e) {
                throw new NucleusUserException(e.getMessage());
            }
        }
    }
   
    @Override
    public void storeBooleanField(int fieldNumber, boolean value) {
        storeField(fieldNumber, value);
    }
   
    @Override
    public void storeByteField(int fieldNumber, byte value) {
        storeField(fieldNumber, String.valueOf(value));
    }
   
    @Override
    public void storeCharField(int fieldNumber, char value) {
        storeField(fieldNumber, String.valueOf(value));
    }
   
    @Override
    public void storeDoubleField(int fieldNumber, double value) {
        storeField(fieldNumber, value);
    }
   
    @Override
    public void storeFloatField(int fieldNumber, float value) {
        storeDoubleField(fieldNumber, value);
    }
   
    @Override
    public void storeIntField(int fieldNumber, int value) {
        storeField(fieldNumber, value);
    }
   
    @Override
    public void storeLongField(int fieldNumber, long value) {
        storeObjectField(fieldNumber, value);
    }
   
    @Override
    public void storeObjectField(int fieldNumber, Object value) {
        if (value instanceof Long) {
            storeField(fieldNumber, value.toString());
        } else if (value instanceof Float) {
            storeField(fieldNumber, ((Float) value).doubleValue());
        } else if (value instanceof Short) {
            storeField(fieldNumber, ((Short) value).intValue());
        } else if (value instanceof Byte) {
            storeField(fieldNumber, ((Byte) value).toString());
        } else if (value instanceof BigInteger) {
            storeField(fieldNumber, value.toString());
        } else if (value instanceof Character) {
            storeField(fieldNumber, ((Character) value).toString());
        } else {
            storeField(fieldNumber, value);
        }
    }
   
    @Override
    public void storeShortField(int fieldNumber, short value) {
        storeIntField(fieldNumber, value);
    }
   
    @Override
    public void storeStringField(int fieldNumber, String value) {
        storeField(fieldNumber, value);
    }
}
TOP

Related Classes of com.force.sdk.jpa.ForceInsertFieldManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.