/**
         *
         * @param value Value, which should be mapped to the middle-table, either as a relation to another entity,
         * or as a simple value.
         * @param xmlMapping If not <code>null</code>, xml mapping for this value is added to this element.
         * @param queryGeneratorBuilder In case <code>value</code> is a relation to another entity, information about it
         * should be added to the given.
         * @param prefix Prefix for proeprty names of related entities identifiers.
         * @param joinColumns Names of columns to use in the xml mapping, if this array isn't null and has any elements.
         * @return Data for mapping this component.
         */
        //@SuppressWarnings({"unchecked"})
        private MiddleComponentData AddValueToMiddleTable(IValue value, XmlElement xmlMapping,
            QueryGeneratorBuilder queryGeneratorBuilder,
            String prefix, JoinColumnAttribute[] joinColumns)
        {
            IType type = value.Type;
            if (type is ManyToOneType) {
                String prefixRelated = prefix + "_";

                String referencedEntityName = MappingTools.getReferencedEntityName(value);

                IdMappingData referencedIdMapping = mainGenerator.GetReferencedIdMappingData(referencingEntityName,
                        referencedEntityName, propertyAuditingData, true);

                // Adding related-entity (in this case: the referenced entities id) id mapping to the xml only if the
                // relation isn't inverse (so when <code>xmlMapping</code> is not null).
                if (xmlMapping != null) {
                    AddRelatedToXmlMapping(xmlMapping, prefixRelated,
                            joinColumns != null && joinColumns.Length > 0
                                    ? MetadataTools.GetColumnNameEnumerator(joinColumns)
                                    : MetadataTools.GetColumnNameEnumerator(value.ColumnIterator.GetEnumerator()),
                            referencedIdMapping);
                }

                // Storing the id data of the referenced entity: original mapper, prefixed mapper and entity name.
                MiddleIdData referencedIdData = CreateMiddleIdData(referencedIdMapping,
                        prefixRelated, referencedEntityName);
                // And adding it to the generator builder.
                queryGeneratorBuilder.AddRelation(referencedIdData);

                return new MiddleComponentData(new MiddleRelatedComponentMapper(referencedIdData),
                        queryGeneratorBuilder.CurrentIndex);
            } else {
                // Last but one parameter: collection components are always insertable
                bool mapped = mainGenerator.BasicMetadataGenerator.AddBasic(xmlMapping,
                        new PropertyAuditingData(prefix, "field", ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null, false),
                        value, null, true, true);

                if (mapped) {
                    // Simple values are always stored in the first item of the array returned by the query generator.
                    return new MiddleComponentData(new MiddleSimpleComponentMapper(mainGenerator.VerEntCfg, prefix), 0);
                } else {
                    mainGenerator.ThrowUnsupportedTypeException(type, referencingEntityName, propertyName);
                    // Impossible to get here.
                    throw new AssertionFailure();
                }
            }
        }
        private void AddWithMiddleTable()
        {
            log.Debug("Adding audit mapping for property " + referencingEntityName + "." + propertyName +
                    ": collection with a join table.");

            // Generating the name of the middle table
            String auditMiddleTableName;
            String auditMiddleEntityName;
            if (!String.IsNullOrEmpty(propertyAuditingData.JoinTable.Name))
            {
                auditMiddleTableName = propertyAuditingData.JoinTable.Name;
                auditMiddleEntityName = propertyAuditingData.JoinTable.Name;
            }
            else
            {
                String middleTableName = GetMiddleTableName(propertyValue, referencingEntityName);
                auditMiddleTableName = mainGenerator.VerEntCfg.GetAuditTableName(null, middleTableName);
                auditMiddleEntityName = mainGenerator.VerEntCfg.GetAuditEntityName(middleTableName);
            }

            log.Debug("Using join table name: " + auditMiddleTableName);

            // Generating the XML mapping for the middle entity, only if the relation isn't inverse.
            // If the relation is inverse, will be later checked by comparing middleEntityXml with null.
            XmlElement middleEntityXml;
            if (!propertyValue.IsInverse)
            {
                // Generating a unique middle entity name
                auditMiddleEntityName = mainGenerator.AuditEntityNameRegister.createUnique(auditMiddleEntityName);

                // Registering the generated name
                mainGenerator.AuditEntityNameRegister.register(auditMiddleEntityName);

                middleEntityXml = CreateMiddleEntityXml(auditMiddleTableName, auditMiddleEntityName, propertyValue.Where);
            }
            else
            {
                middleEntityXml = null;
            }

            // ******
            // Generating the mapping for the referencing entity (it must be an entity).
            // ******
            // Getting the id-mapping data of the referencing entity (the entity that "owns" this collection).
            IdMappingData referencingIdMapping = referencingEntityConfiguration.IdMappingData;

            // Only valid for an inverse relation; null otherwise.
            String mappedBy;

            // The referencing prefix is always for a related entity. So it has always the "_" at the end added.
            String referencingPrefixRelated;
            String referencedPrefix;

            if (propertyValue.IsInverse)
            {
                // If the relation is inverse, then referencedEntityName is not null.
                mappedBy = GetMappedBy(propertyValue.CollectionTable, mainGenerator.Cfg.GetClassMapping(referencedEntityName));

                referencingPrefixRelated = mappedBy + "_";
                referencedPrefix = StringTools.GetLastComponent(referencedEntityName);
            }
            else
            {
                mappedBy = null;

                referencingPrefixRelated = StringTools.GetLastComponent(referencingEntityName) + "_";
                referencedPrefix = referencedEntityName == null ? "element" : propertyName;
            }

            // Storing the id data of the referencing entity: original mapper, prefixed mapper and entity name.
            MiddleIdData referencingIdData = CreateMiddleIdData(referencingIdMapping,
                    referencingPrefixRelated, referencingEntityName);

            // Creating a query generator builder, to which additional id data will be added, in case this collection
            // references some entities (either from the element or index). At the end, this will be used to build
            // a query generator to read the raw data collection from the middle table.
            QueryGeneratorBuilder queryGeneratorBuilder = new QueryGeneratorBuilder(mainGenerator.GlobalCfg,
                    mainGenerator.VerEntCfg, referencingIdData, auditMiddleEntityName);

            // Adding the XML mapping for the referencing entity, if the relation isn't inverse.
            if (middleEntityXml != null)
            {
                // Adding related-entity (in this case: the referencing's entity id) id mapping to the xml.
                AddRelatedToXmlMapping(middleEntityXml, referencingPrefixRelated,
                        MetadataTools.GetColumnNameEnumerator(propertyValue.Key.ColumnIterator.GetEnumerator()),
                        referencingIdMapping);
            }

            // ******
            // Generating the element mapping.
            // ******
            MiddleComponentData elementComponentData = AddValueToMiddleTable(propertyValue.Element, middleEntityXml,
                    queryGeneratorBuilder, referencedPrefix, propertyAuditingData.JoinTable.InverseJoinColumns);

            // ******
            // Generating the index mapping, if an index exists.
            // ******
            MiddleComponentData indexComponentData = AddIndex(middleEntityXml, queryGeneratorBuilder);

            // ******
            // Generating the property mapper.
            // ******
            // Building the query generator.
            IRelationQueryGenerator queryGenerator = queryGeneratorBuilder.Build(new Collection<MiddleComponentData>{elementComponentData, indexComponentData});

            // Creating common data
            CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData(
                    mainGenerator.VerEntCfg, auditMiddleEntityName,
                    propertyAuditingData.getPropertyData(),
                    referencingIdData, queryGenerator);

            // Checking the type of the collection and adding an appropriate mapper.
            AddMapper(commonCollectionMapperData, elementComponentData, indexComponentData);

            // ******
            // Storing information about this relation.
            // ******
            StoreMiddleEntityRelationInformation(mappedBy);
        }
 private MiddleComponentData AddIndex(XmlElement middleEntityXml, QueryGeneratorBuilder queryGeneratorBuilder)
 {
     if (propertyValue is IndexedCollection)
     {
         IndexedCollection indexedValue = (IndexedCollection)propertyValue;
         String mapKey = propertyAuditingData.MapKey;
         if (mapKey == null)
         {
             // This entity doesn't specify a javax.persistence.MapKey. Mapping it to the middle entity.
             return AddValueToMiddleTable(indexedValue.Index, middleEntityXml,
                     queryGeneratorBuilder, "mapkey", null);
         }
         else
         {
             IdMappingData referencedIdMapping =
                     mainGenerator.EntitiesConfigurations[referencedEntityName].IdMappingData;
             int currentIndex = queryGeneratorBuilder == null ? 0 : queryGeneratorBuilder.CurrentIndex;
             if ("".Equals(mapKey))
             {
                 // The key of the map is the id of the entity.
                 return new MiddleComponentData(new MiddleMapKeyIdComponentMapper(mainGenerator.VerEntCfg,
                         referencedIdMapping.IdMapper), currentIndex);
             }
             else
             {
                 // The key of the map is a property of the entity.
                 return new MiddleComponentData(new MiddleMapKeyPropertyComponentMapper(mapKey,
                         propertyAuditingData.AccessType), currentIndex);
             }
         }
     }
     else
     {
         // No index - creating a dummy mapper.
         return new MiddleComponentData(new MiddleDummyComponentMapper(), 0);
     }
 }