/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     05/05/2009 Andrei Ilitchev
//       - JPA 2.0 - OrderedList support.
package org.eclipse.persistence.testing.models.orderedlist;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.eclipse.persistence.sessions.DatabaseLogin;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.testing.models.orderedlist.EmployeeSystem.ChangeTracking;
import org.eclipse.persistence.testing.models.orderedlist.EmployeeSystem.JoinFetchOrBatchRead;
import org.eclipse.persistence.annotations.BatchFetchType;
import org.eclipse.persistence.annotations.OrderCorrectionType;
import org.eclipse.persistence.descriptors.*;
import org.eclipse.persistence.indirection.IndirectList;
import org.eclipse.persistence.mappings.*;

public class EmployeeProject extends org.eclipse.persistence.sessions.Project {

    boolean useListOrderField;
    boolean useIndirection;
    boolean isPrivatelyOwned;
    boolean useSecondaryTable;
    boolean useVarcharOrder;
    ChangeTracking changeTracking;
    OrderCorrectionType orderCorrectionType;
    boolean shouldOverrideContainerPolicy;
    JoinFetchOrBatchRead joinFetchOrBatchRead;

    public EmployeeProject(boolean useListOrderField, boolean useIndirection, boolean isPrivatelyOwned, boolean useSecondaryTable, boolean useVarcharOrder, ChangeTracking changeTracking, OrderCorrectionType orderCorrectionType, boolean shouldOverrideContainerPolicy, JoinFetchOrBatchRead joinFetchOrBatchRead) {
        this.useListOrderField = useListOrderField;
        this.useIndirection = useIndirection;
        this.isPrivatelyOwned = isPrivatelyOwned;
        this.useSecondaryTable = useSecondaryTable;
        this.useVarcharOrder = useVarcharOrder;
        this.changeTracking = changeTracking;
        this.orderCorrectionType = orderCorrectionType;
        this.shouldOverrideContainerPolicy = shouldOverrideContainerPolicy;
        this.joinFetchOrBatchRead = joinFetchOrBatchRead;

        setName("OL_Employee");
        setDatasourceLogin(new DatabaseLogin());

        addDescriptor(buildChildDescriptor());
        addDescriptor(buildEmployeeDescriptor());
        addDescriptor(buildLargeProjectDescriptor());
        // currently attribute change tracking is incompatible with AggregateCollectionMapping
        if(changeTracking != ChangeTracking.ATTRIBUTE) {
            addDescriptor(buildPhoneNumberDescriptor());
        }
        addDescriptor(buildProjectDescriptor());
        addDescriptor(buildSmallProjectDescriptor());
    }

    public ClassDescriptor buildChildDescriptor() {
        RelationalDescriptor descriptor = new RelationalDescriptor();
        descriptor.setJavaClass(Child.class);
        descriptor.addTableName("OL_CHILD");
        descriptor.addTableName("OL_ALLOWANCE");
        descriptor.addPrimaryKeyFieldName("CHILD_ID");

        descriptor.addForeignKeyFieldNameForMultipleTable("OL_ALLOWANCE.OWNER_CHILD_ID", "OL_CHILD.CHILD_ID");

        descriptor.useSoftCacheWeakIdentityMap();
        descriptor.setIdentityMapSize(50);
        descriptor.setSequenceNumberFieldName("CHILD_ID");
        descriptor.setSequenceNumberName("OL_CHILD_SEQ");
        descriptor.setAlias("OL_Child");

        // Query Manager.
        descriptor.getQueryManager().checkCacheForDoesExist();

        // Mappings.
        DirectToFieldMapping firstNameMapping = new DirectToFieldMapping();
        firstNameMapping.setAttributeName("firstName");
        firstNameMapping.setFieldName("F_NAME");
        firstNameMapping.setNullValue("");
        descriptor.addMapping(firstNameMapping);

        DirectToFieldMapping lastNameMapping = new DirectToFieldMapping();
        lastNameMapping.setAttributeName("lastName");
        lastNameMapping.setFieldName("L_NAME");
        lastNameMapping.setNullValue("");
        descriptor.addMapping(lastNameMapping);

        DirectToFieldMapping idMapping = new DirectToFieldMapping();
        idMapping.setAttributeName("id");
        idMapping.setFieldName("CHILD_ID");
        descriptor.addMapping(idMapping);

        DirectToFieldMapping allowanceMapping = new DirectToFieldMapping();
        allowanceMapping.setAttributeName("allowance");
        allowanceMapping.setFieldName("OL_ALLOWANCE.ALLOWANCE");
        descriptor.addMapping(allowanceMapping);

        return descriptor;
    }

    public ClassDescriptor buildEmployeeDescriptor() {
        RelationalDescriptor descriptor = new RelationalDescriptor();
        descriptor.setJavaClass(Employee.class);
        descriptor.addTableName("OL_EMPLOYEE");
        descriptor.addTableName("OL_SALARY");
        descriptor.addPrimaryKeyFieldName("OL_EMPLOYEE.EMP_ID");

        descriptor.addForeignKeyFieldNameForMultipleTable("OL_SALARY.OWNER_EMP_ID", "OL_EMPLOYEE.EMP_ID");

        // Descriptor Properties.
        descriptor.useSoftCacheWeakIdentityMap();
        descriptor.setIdentityMapSize(100);
        descriptor.setSequenceNumberFieldName("EMP_ID");
        descriptor.setSequenceNumberName("OL_EMP_SEQ");
        VersionLockingPolicy lockingPolicy = new VersionLockingPolicy();
        lockingPolicy.setWriteLockFieldName("VERSION");
        descriptor.setOptimisticLockingPolicy(lockingPolicy);
        descriptor.setAlias("OL_Employee");

        // Cache Invalidation Policy
        // Query Manager.
        descriptor.getQueryManager().checkCacheForDoesExist();

        // Named Queries.
        // Event Manager.
        // Mappings.
        DirectToFieldMapping firstNameMapping = new DirectToFieldMapping();
        firstNameMapping.setAttributeName("firstName");
        firstNameMapping.setFieldName("F_NAME");
        firstNameMapping.setNullValue("");
        descriptor.addMapping(firstNameMapping);

        DirectToFieldMapping idMapping = new DirectToFieldMapping();
        idMapping.setAttributeName("id");
        idMapping.setFieldName("EMP_ID");
        descriptor.addMapping(idMapping);

        DirectToFieldMapping lastNameMapping = new DirectToFieldMapping();
        lastNameMapping.setAttributeName("lastName");
        lastNameMapping.setFieldName("L_NAME");
        lastNameMapping.setNullValue("");
        descriptor.addMapping(lastNameMapping);

        DirectToFieldMapping salaryMapping = new DirectToFieldMapping();
        salaryMapping.setAttributeName("salary");
        salaryMapping.setFieldName("OL_SALARY.SALARY");
        descriptor.addMapping(salaryMapping);

        DirectCollectionMapping responsibilitiesListMapping = new DirectCollectionMapping();
        responsibilitiesListMapping.setAttributeName("responsibilitiesList");
        if(useListOrderField) {
            // target foreign key and listOrderField must be in the same table, so
            // either specify listOrderField with target foreign key table
//            responsibilitiesListMapping.setListOrderFieldName("OL_RESPONS.RESPONS_ORDER");
            // or don't specify listOrderField's table at all - it will be defaulted to foreign key table.
            if(useVarcharOrder) {
                responsibilitiesListMapping.setListOrderFieldName("RESPONS_ORDER_VARCHAR");
            } else {
                responsibilitiesListMapping.setListOrderFieldName("RESPONS_ORDER");
            }
            responsibilitiesListMapping.setOrderCorrectionType(orderCorrectionType);
        }
        if(useIndirection) {
            responsibilitiesListMapping.useTransparentList();
        } else {
            if(changeTracking == ChangeTracking.ATTRIBUTE) {
                responsibilitiesListMapping.useCollectionClass(IndirectList.class);
            } else {
                responsibilitiesListMapping.useCollectionClass(ArrayList.class);
            }
            responsibilitiesListMapping.dontUseIndirection();
        }
        responsibilitiesListMapping.setReferenceTableName("OL_RESPONS");
        responsibilitiesListMapping.setDirectFieldName("OL_RESPONS.DESCRIP");
        responsibilitiesListMapping.addReferenceKeyFieldName("OL_RESPONS.EMP_ID", "EMP_ID");
        setJoinFetchOrBatchRead(responsibilitiesListMapping);
        descriptor.addMapping(responsibilitiesListMapping);

        // managedEmployees have nothing: no managedEmployees, no children, no projects, no responsibilities, no phones -
        // can't read them using INNER_JOIN
        if(this.joinFetchOrBatchRead != JoinFetchOrBatchRead.INNER_JOIN) {
            OneToOneMapping managerMapping = new OneToOneMapping();
            managerMapping.setAttributeName("manager");
            managerMapping.setReferenceClass(Employee.class);
            if(useSecondaryTable) {
                managerMapping.addForeignKeyFieldName("OL_SALARY.MANAGER_ID", "EMP_ID");
            } else {
                managerMapping.addForeignKeyFieldName("MANAGER_ID", "EMP_ID");
            }
            descriptor.addMapping(managerMapping);

            OneToManyMapping managedEmployeesMapping = new OneToManyMapping();
            managedEmployeesMapping.setAttributeName("managedEmployees");
            managedEmployeesMapping.setReferenceClass(Employee.class);
            if(isPrivatelyOwned) {
                managedEmployeesMapping.privateOwnedRelationship();
            }
            if(useIndirection) {
                managedEmployeesMapping.useTransparentList();
            } else {
                if(changeTracking == ChangeTracking.ATTRIBUTE) {
                    managedEmployeesMapping.useCollectionClass(IndirectList.class);
                } else {
                    managedEmployeesMapping.useCollectionClass(ArrayList.class);
                }
                managedEmployeesMapping.dontUseIndirection();
            }
            if(useListOrderField) {
                // target foreign key and listOrderField must be in the same table, so
                // either specify listOrderField with target foreign key table
/*                if(useSecondaryTable) {
                    managedEmployeesMapping.setListOrderFieldName("OL_SALARY.MANAGED_ORDER");
                } else {
                    managedEmployeesMapping.setListOrderFieldName("OL_EMPLOYEE.MANAGED_ORDER");
                }*/
                // or don't specify listOrderField's table at all - it will be defaulted to foreign key table.
                // If secondary table used then the field is in SALARY table, otherwise in EMPLOYEE table
                // (both tables have MANAGED_ORDER field, only one of them used at a time).
                if(useVarcharOrder) {
                    managedEmployeesMapping.setListOrderFieldName("MANAGED_ORDER_VARCHAR");
                } else {
                    managedEmployeesMapping.setListOrderFieldName("MANAGED_ORDER");
                }
                managedEmployeesMapping.setOrderCorrectionType(orderCorrectionType);
            }
            if(useSecondaryTable) {
                managedEmployeesMapping.addTargetForeignKeyFieldName("OL_SALARY.MANAGER_ID", "EMP_ID");
            } else {
                managedEmployeesMapping.addTargetForeignKeyFieldName("MANAGER_ID", "EMP_ID");
            }
            setJoinFetchOrBatchRead(managedEmployeesMapping);
            descriptor.addMapping(managedEmployeesMapping);
        }

        OneToManyMapping childrenMapping = new UnidirectionalOneToManyMapping();
        childrenMapping.setAttributeName("children");
        childrenMapping.setReferenceClass(Child.class);
        if(isPrivatelyOwned) {
            childrenMapping.privateOwnedRelationship();
        }
        if(useListOrderField) {
            // target foreign key and listOrderField must be in the same table, so
            // either specify listOrderField with target foreign key table
/*            if(useSecondaryTable) {
                childrenMapping.setListOrderFieldName("OL_ALLOWANCE.CHILDREN_ORDER");
            } else {
                childrenMapping.setListOrderFieldName("OL_CHILD.CHILDREN_ORDER");
            }*/
            // or don't specify listOrderField's table at all - it will be defaulted to foreign key table.
            // If secondary table used then the field is in ALLOWANCE table, otherwise in CHILD table
            // (both tables have CHILDREN_ORDER field, only one of them used at a time).
            if(useVarcharOrder) {
                childrenMapping.setListOrderFieldName("CHILDREN_ORDER_VARCHAR");
            } else {
                childrenMapping.setListOrderFieldName("CHILDREN_ORDER");
            }
            childrenMapping.setOrderCorrectionType(orderCorrectionType);
        }
        if(useIndirection) {
            childrenMapping.useTransparentList();
        } else {
            if(changeTracking == ChangeTracking.ATTRIBUTE) {
                childrenMapping.useCollectionClass(IndirectList.class);
            } else {
                childrenMapping.useCollectionClass(Vector.class);
            }
            childrenMapping.dontUseIndirection();
        }
        if(useSecondaryTable) {
            childrenMapping.addTargetForeignKeyFieldName("OL_ALLOWANCE.PARENT_ID", "EMP_ID");
        } else {
            childrenMapping.addTargetForeignKeyFieldName("OL_CHILD.PARENT_ID", "EMP_ID");
        }
        setJoinFetchOrBatchRead(childrenMapping);
        descriptor.addMapping(childrenMapping);

        // currently attribute change tracking is incompatible with AggregateCollectionMapping
        if(changeTracking != ChangeTracking.ATTRIBUTE) {
            AggregateCollectionMapping phoneNumbersMapping = new AggregateCollectionMapping();
            phoneNumbersMapping.setAttributeName("phoneNumbers");
            phoneNumbersMapping.setReferenceClass(PhoneNumber.class);
            phoneNumbersMapping.addTargetForeignKeyFieldName("OL_PHONE.OWNER_ID", "OL_EMPLOYEE.EMP_ID");
            phoneNumbersMapping.addFieldNameTranslation("OL_PHONE.OWNER_ID", "OWNER_ID");
            phoneNumbersMapping.addFieldNameTranslation("OL_PHONE.AREA_CODE", "AREA_CODE");
            phoneNumbersMapping.addFieldNameTranslation("OL_PHONE.PNUMBER", "PNUMBER");
            phoneNumbersMapping.addFieldNameTranslation("OL_PHONE.TYPE", "TYPE");
            if(useListOrderField) {
                // target foreign key and listOrderField must be in the same table, so
                // either specify listOrderField with target foreign key table
//                phoneNumbersMapping.setListOrderFieldName("OL_PHONE.PHONE_ORDER");
                // or don't specify listOrderField's table at all - it will be defaulted to foreign key table.
                if(useVarcharOrder) {
                    phoneNumbersMapping.setListOrderFieldName("PHONE_ORDER_VARCHAR");
                } else {
                    phoneNumbersMapping.setListOrderFieldName("PHONE_ORDER");
                }
                phoneNumbersMapping.setOrderCorrectionType(orderCorrectionType);
            }
            if(useIndirection) {
                phoneNumbersMapping.useTransparentList();
            } else {
                if(changeTracking == ChangeTracking.ATTRIBUTE) {
                    phoneNumbersMapping.useCollectionClass(IndirectList.class);
                } else {
                    phoneNumbersMapping.useCollectionClass(ArrayList.class);
                }
                phoneNumbersMapping.dontUseIndirection();
            }
            setJoinFetchOrBatchRead(phoneNumbersMapping);
            descriptor.addMapping(phoneNumbersMapping);
        }

        ManyToManyMapping projectsMapping = new ManyToManyMapping();
        projectsMapping.setAttributeName("projects");
        projectsMapping.setReferenceClass(Project.class);
        if(isPrivatelyOwned) {
            projectsMapping.privateOwnedRelationship();
        }
        if(useIndirection) {
            projectsMapping.useTransparentList();
        } else {
            if(changeTracking == ChangeTracking.ATTRIBUTE) {
                projectsMapping.useCollectionClass(IndirectList.class);
            } else {
                projectsMapping.useCollectionClass(ArrayList.class);
            }
            projectsMapping.dontUseIndirection();
        }
        if(useListOrderField) {
            // target foreign key and listOrderField must be in the same table, so
            // either specify listOrderField with target foreign key table
//            projectsMapping.setListOrderFieldName("OL_PROJ_EMP.PROJ_ORDER");
            // or don't specify listOrderField's table at all - it will be defaulted to foreign key table.
            if(useVarcharOrder) {
                projectsMapping.setListOrderFieldName("PROJ_ORDER_VARCHAR");
            } else {
                projectsMapping.setListOrderFieldName("PROJ_ORDER");
            }
            projectsMapping.setOrderCorrectionType(orderCorrectionType);
        }
        projectsMapping.setRelationTableName("OL_PROJ_EMP");
        projectsMapping.addSourceRelationKeyFieldName("OL_PROJ_EMP.EMP_ID", "EMP_ID");
        projectsMapping.addTargetRelationKeyFieldName("OL_PROJ_EMP.PROJ_ID", "OL_PROJECT.PROJ_ID");
        setJoinFetchOrBatchRead(projectsMapping);
        descriptor.addMapping(projectsMapping);

        // Currently projectsLed is not used in the tests - that breaks INNER_JOIN
/*        if(this.joinFetchOrBatchRead != JoinFetchOrBatchRead.INNER_JOIN) {
            OneToManyMapping projectsLedMapping = new OneToManyMapping();
            projectsLedMapping.setAttributeName("projectsLed");
            projectsLedMapping.setReferenceClass(Project.class);
            if(useListOrderField) {
                projectsLedMapping.setListOrderFieldName("OL_PROJECT.PROJECTS_LED_ORDER");
                projectsLedMapping.setOrderCorrectionType(orderCorrectionType);
            }
            if(useIndirection) {
                projectsLedMapping.useTransparentList();
            } else {
                if(changeTracking == ChangeTracking.ATTRIBUTE) {
                    projectsLedMapping.useCollectionClass(IndirectList.class);
                } else {
                    projectsLedMapping.useCollectionClass(ArrayList.class);
                }
                projectsLedMapping.dontUseIndirection();
            }
            projectsLedMapping.addTargetForeignKeyFieldName("OL_PROJECT.LEADER_ID", "EMP_ID");
            setJoinFetchOrBatchRead(projectsLedMapping);
            descriptor.addMapping(projectsLedMapping);
        }*/

        return descriptor;
    }

    public ClassDescriptor buildPhoneNumberDescriptor() {
        RelationalDescriptor descriptor = new RelationalDescriptor();
        descriptor.setJavaClass(PhoneNumber.class);
        // table name is overridden in AggregateCollectionMapping
        descriptor.addTableName("OL_AGGREGATE_PHONE");

        // Descriptor Properties.
        descriptor.descriptorIsAggregate();

        // Mappings.
        DirectToFieldMapping areaCodeMapping = new DirectToFieldMapping();
        areaCodeMapping.setAttributeName("areaCode");
        areaCodeMapping.setFieldName("AREA_CODE");
        descriptor.addMapping(areaCodeMapping);

        DirectToFieldMapping numberMapping = new DirectToFieldMapping();
        numberMapping.setAttributeName("number");
        numberMapping.setFieldName("P_NUMBER");
        descriptor.addMapping(numberMapping);

        DirectToFieldMapping typeMapping = new DirectToFieldMapping();
        typeMapping.setAttributeName("type");
        typeMapping.setFieldName("TYPE");
        descriptor.addMapping(typeMapping);

/*        OneToOneMapping ownerMapping = new OneToOneMapping();
        ownerMapping.setAttributeName("owner");
        ownerMapping.setReferenceClass(Employee.class);
        ownerMapping.useBasicIndirection();
        ownerMapping.addForeignKeyFieldName("OWNER_ID", "OL_EMPLOYEE.EMP_ID");
        descriptor.addMapping(ownerMapping);*/

        return descriptor;
    }

    public ClassDescriptor buildProjectDescriptor() {
        RelationalDescriptor descriptor = new RelationalDescriptor();
        descriptor.setJavaClass(Project.class);
        descriptor.addTableName("OL_PROJECT");
        descriptor.addPrimaryKeyFieldName("OL_PROJECT.PROJ_ID");

        // Inheritance Properties.
        descriptor.getInheritancePolicy().setClassIndicatorFieldName("PROJ_TYPE");
        descriptor.getInheritancePolicy().addClassIndicator(SmallProject.class, "S");
        descriptor.getInheritancePolicy().addClassIndicator(LargeProject.class, "L");

        // Descriptor Properties.
        descriptor.useSoftCacheWeakIdentityMap();
        descriptor.setIdentityMapSize(100);
        descriptor.setSequenceNumberFieldName("PROJ_ID");
        descriptor.setSequenceNumberName("OL_PROJ_SEQ");
        VersionLockingPolicy lockingPolicy = new VersionLockingPolicy();
        lockingPolicy.setWriteLockFieldName("VERSION");
        descriptor.setOptimisticLockingPolicy(lockingPolicy);
        descriptor.setAlias("OL_Project");

        // Cache Invalidation Policy
        // Query Manager.
        descriptor.getQueryManager().checkCacheForDoesExist();

        // Named Queries.
        // Event Manager.
        // Mappings.
        DirectToFieldMapping descriptionMapping = new DirectToFieldMapping();
        descriptionMapping.setAttributeName("description");
        descriptionMapping.setFieldName("DESCRIP");
        descriptionMapping.setNullValue("");
        descriptor.addMapping(descriptionMapping);

        DirectToFieldMapping idMapping = new DirectToFieldMapping();
        idMapping.setAttributeName("id");
        idMapping.setFieldName("PROJ_ID");
        descriptor.addMapping(idMapping);

        DirectToFieldMapping nameMapping = new DirectToFieldMapping();
        nameMapping.setAttributeName("name");
        nameMapping.setFieldName("PROJ_NAME");
        nameMapping.setNullValue("");
        descriptor.addMapping(nameMapping);

        OneToOneMapping teamLeaderMapping = new OneToOneMapping();
        teamLeaderMapping.setAttributeName("teamLeader");
        teamLeaderMapping.setReferenceClass(Employee.class);
        teamLeaderMapping.useBasicIndirection();
        teamLeaderMapping.addForeignKeyFieldName("LEADER_ID", "OL_EMPLOYEE.EMP_ID");
        descriptor.addMapping(teamLeaderMapping);

        // ReadOnly - mapped employees attribute on Project is not automatically updated,
        // but only in case of privatelyOwned mapping it fails (many) tests:
        // that happens because compare operates differently on privately owned objects -
        // it compares objects (instead of comparing just their pks in non privately owned case).
        //
        // Also excluded this mapping from inner and outer joins and batch reading configurations.
        // If the mapping is enabled, in case of outer joins SimpleIndexTest keeps running out of memory;
        // inner joins and batch reading configuration generate more queries as well (but not as bad as outer join case).
        //
        // May be this mapping should be excluded for all configurations.
        if(!isPrivatelyOwned && this.joinFetchOrBatchRead == JoinFetchOrBatchRead.NONE) {
            ManyToManyMapping employeesMapping = new ManyToManyMapping();
            employeesMapping.setAttributeName("employees");
            employeesMapping.setReferenceClass(Employee.class);
            if(useIndirection) {
                employeesMapping.useTransparentList();
            } else {
                if(changeTracking == ChangeTracking.ATTRIBUTE) {
                    employeesMapping.useCollectionClass(IndirectList.class);
                } else {
                    employeesMapping.useCollectionClass(ArrayList.class);
                }
                employeesMapping.dontUseIndirection();
            }
            employeesMapping.readOnly();
            employeesMapping.setRelationTableName("OL_PROJ_EMP");
            employeesMapping.addSourceRelationKeyFieldName("OL_PROJ_EMP.PROJ_ID", "PROJ_ID");
            employeesMapping.addTargetRelationKeyFieldName("OL_PROJ_EMP.EMP_ID", "OL_EMPLOYEE.EMP_ID");
            descriptor.addMapping(employeesMapping);
        }

        return descriptor;
    }

    public ClassDescriptor buildSmallProjectDescriptor() {
        RelationalDescriptor descriptor = new RelationalDescriptor();
        descriptor.setJavaClass(SmallProject.class);

        // Inheritance Properties.
        descriptor.getInheritancePolicy().setParentClass(Project.class);
        descriptor.getInheritancePolicy().dontReadSubclassesOnQueries();

        // Descriptor Properties.
        descriptor.setAlias("OL_SmallProject");

        // Cache Invalidation Policy
        // Query Manager.
        descriptor.getQueryManager().checkCacheForDoesExist();

        // Named Queries.
        // Event Manager.
        return descriptor;
    }

    public ClassDescriptor buildLargeProjectDescriptor() {
        RelationalDescriptor descriptor = new RelationalDescriptor();
        descriptor.setJavaClass(LargeProject.class);
        descriptor.addTableName("OL_LPROJECT");

        // Inheritance Properties.
        descriptor.getInheritancePolicy().setParentClass(Project.class);
        descriptor.getInheritancePolicy().dontReadSubclassesOnQueries();

        // Descriptor Properties.
        descriptor.setAlias("OL_LargeProject");

        // Cache Invalidation Policy
        // Query Manager.
        descriptor.getQueryManager().checkCacheForDoesExist();

        // Named Queries.
        // Event Manager.
        // Mappings.
        DirectToFieldMapping budgetMapping = new DirectToFieldMapping();
        budgetMapping.setAttributeName("budget");
        budgetMapping.setFieldName("OL_LPROJECT.BUDGET");
        descriptor.addMapping(budgetMapping);

        DirectToFieldMapping milestoneVersionMapping = new DirectToFieldMapping();
        milestoneVersionMapping.setAttributeName("milestoneVersion");
        milestoneVersionMapping.setFieldName("OL_LPROJECT.MILESTONE");
        descriptor.addMapping(milestoneVersionMapping);

        return descriptor;
    }

    void setJoinFetchOrBatchRead(ForeignReferenceMapping mapping) {
        if(joinFetchOrBatchRead == JoinFetchOrBatchRead.INNER_JOIN) {
            mapping.setJoinFetch(ForeignReferenceMapping.INNER_JOIN);
        } else if(joinFetchOrBatchRead == JoinFetchOrBatchRead.OUTER_JOIN) {
            mapping.setJoinFetch(ForeignReferenceMapping.OUTER_JOIN);
        } else if(joinFetchOrBatchRead == JoinFetchOrBatchRead.BATCH_FETCH) {
            mapping.setBatchFetchType(BatchFetchType.JOIN);
        } else if(joinFetchOrBatchRead == JoinFetchOrBatchRead.BATCH_IN_FETCH) {
            mapping.setBatchFetchType(BatchFetchType.IN);
        } else if(joinFetchOrBatchRead == JoinFetchOrBatchRead.BATCH_EXISTS_FETCH) {
            mapping.setBatchFetchType(BatchFetchType.EXISTS);
        }
    }

    List<CollectionMapping> getListOrderMappings() {
        List<CollectionMapping> list = new ArrayList();
        Iterator<ClassDescriptor> it = this.getDescriptors().values().iterator();
        while(it.hasNext()) {
            list.addAll(getListOrderMappings(it.next()));
        }
        return list;
    }

    static List<CollectionMapping> getListOrderMappings(DatabaseSession session) {
        List<CollectionMapping> list = new ArrayList();
        list.addAll(getListOrderMappings(session.getDescriptor(Employee.class)));
        list.addAll(getListOrderMappings(session.getDescriptor(Project.class)));
        return list;
    }

    static List<CollectionMapping> getListOrderMappings(ClassDescriptor desc) {
        List<CollectionMapping> list = new ArrayList();
        List<DatabaseMapping> mappings = desc.getMappings();
        for(int i=0; i < mappings.size(); i++) {
            if(mappings.get(i).isCollectionMapping()) {
                CollectionMapping collectionMapping = (CollectionMapping)mappings.get(i);
                if(collectionMapping.getListOrderField() != null) {
                    list.add(collectionMapping);
                }
             }
        }
        return list;
    }
}
