<commit_before>// 
// Copyright (c) 2003-2006 <NAME> <<EMAIL>>
// 
// 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. 
// 
// 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.
// 

using System;
using System.Data;
using System.Collections;

using Sooda.Schema;
using Sooda.Collections;
using Sooda.Caching;
using Sooda.QL;
using Sooda.Logging;

namespace Sooda.ObjectMapper
{
    public class SoodaObjectOneToManyCollection : IList, ISoodaObjectList, ISoodaObjectListInternal
    {
        private static Logger logger = LogManager.GetLogger("Sooda.OneToManyCollection");

        private SoodaObjectToInt32Association items = null;
        private SoodaObjectToObjectAssociation tempItems = null;
        private SoodaObjectCollection itemsArray = null;
        private SoodaObject parentObject;
        private string childRefField;
        protected Sooda.Schema.ClassInfo classInfo;
        private SoodaTransaction transaction;
        private Type childType;
        private SoodaWhereClause additionalWhereClause;
        private bool cached;

        private static object markerAdded = new Object();
        private static object markerRemoved = new Object();

        public SoodaObjectOneToManyCollection(SoodaTransaction tran, Type childType, SoodaObject parentObject, string childRefField, Sooda.Schema.ClassInfo classInfo, SoodaWhereClause additionalWhereClause, bool cached)
        {
            this.classInfo = classInfo;
            this.transaction = tran;
            this.childType = childType;
            this.parentObject = parentObject;
            this.childRefField = childRefField;
            this.additionalWhereClause = additionalWhereClause;
            this.cached = cached;
        }

        public SoodaObject GetItem(int pos)
        {
            if (items == null)
                LoadData();
            return (SoodaObject)itemsArray[pos];
        }

        public int Length
        {
            get
            {
                if (items == null)
                    LoadData();
                return itemsArray.Count;
            }
        }

        public IEnumerator GetEnumerator()
        {
            if (items == null)
                LoadData();
            return items.Keys.GetEnumerator();
        }

        public int Add(object obj)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");

            Type t = childType;
            System.Reflection.PropertyInfo prop = t.GetProperty(childRefField);
            prop.SetValue(obj, parentObject, null);
            return 0;
        }

        public void Remove(object obj)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");

            Type t = childType;
            System.Reflection.PropertyInfo prop = t.GetProperty(childRefField);
            prop.SetValue(obj, null, null);
        }

        public bool Contains(object obj)
        {
            if (obj == null)
                return false;

            Type t = childType;
            System.Reflection.PropertyInfo prop = t.GetProperty(childRefField);
            return parentObject == prop.GetValue(obj, null);
        }

        public void InternalAdd(SoodaObject c)
        {
            if (items == null)
            {
                if (tempItems == null)
                    tempItems = new SoodaObjectToObjectAssociation();
                tempItems[c] = markerAdded;
                return;
            }

            if (items.Contains(c))
                return;

            int pos = itemsArray.Add(c);
            items.Add(c, pos);
        }

        public void InternalRemove(SoodaObject c)
        {
            if (items == null)
            {
                if (tempItems == null)
                    tempItems = new SoodaObjectToObjectAssociation();
                tempItems[c] = markerRemoved;
                return;
            }

            if (!items.Contains(c))
                throw new InvalidOperationException("Attempt to remove object not in collection");

            int pos = items[c];

            SoodaObject lastObj = itemsArray[itemsArray.Count - 1];
            if (lastObj != c)
            {
                itemsArray[pos] = lastObj;
                items[lastObj] = pos;
            }
            itemsArray.RemoveAt(itemsArray.Count - 1);
            items.Remove(c);
        }<commit_msg>BUGFIX Fixed one-to-many collections with inheritance of child classes. Setting the "parent" field (e.g. in deserialization) could lead to malformed collections.<commit_after>// 
// Copyright (c) 2003-2006 <NAME> <<EMAIL>>
// 
// 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. 
// 
// 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.
// 

using System;
using System.Data;
using System.Collections;

using Sooda.Schema;
using Sooda.Collections;
using Sooda.Caching;
using Sooda.QL;
using Sooda.Logging;

namespace Sooda.ObjectMapper
{
    public class SoodaObjectOneToManyCollection : IList, ISoodaObjectList, ISoodaObjectListInternal
    {
        private static Logger logger = LogManager.GetLogger("Sooda.OneToManyCollection");

        private SoodaObjectToInt32Association items = null;
        private SoodaObjectToObjectAssociation tempItems = null;
        private SoodaObjectCollection itemsArray = null;
        private SoodaObject parentObject;
        private string childRefField;
        protected Sooda.Schema.ClassInfo classInfo;
        private SoodaTransaction transaction;
        private Type childType;
        private SoodaWhereClause additionalWhereClause;
        private bool cached;

        private static readonly object markerAdded = new Object();
        private static readonly object markerRemoved = new Object();

        public SoodaObjectOneToManyCollection(SoodaTransaction tran, Type childType, SoodaObject parentObject, string childRefField, Sooda.Schema.ClassInfo classInfo, SoodaWhereClause additionalWhereClause, bool cached)
        {
            this.classInfo = classInfo;
            this.transaction = tran;
            this.childType = childType;
            this.parentObject = parentObject;
            this.childRefField = childRefField;
            this.additionalWhereClause = additionalWhereClause;
            this.cached = cached;
        }

        public SoodaObject GetItem(int pos)
        {
            if (items == null)
                LoadData();
            return (SoodaObject)itemsArray[pos];
        }

        public int Length
        {
            get
            {
                if (items == null)
                    LoadData();
                return itemsArray.Count;
            }
        }

        public IEnumerator GetEnumerator()
        {
            if (items == null)
                LoadData();
            return items.Keys.GetEnumerator();
        }

        public int Add(object obj)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");

            Type t = childType;
            System.Reflection.PropertyInfo prop = t.GetProperty(childRefField);
            prop.SetValue(obj, parentObject, null);
            return 0;
        }

        public void Remove(object obj)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");

            Type t = childType;
            System.Reflection.PropertyInfo prop = t.GetProperty(childRefField);
            prop.SetValue(obj, null, null);
        }

        public bool Contains(object obj)
        {
            if (obj == null)
                return false;

            Type t = childType;
            System.Reflection.PropertyInfo prop = t.GetProperty(childRefField);
            return parentObject == prop.GetValue(obj, null);
        }

        public void InternalAdd(SoodaObject c)
        {
            if (!childType.IsInstanceOfType(c))
                return;

            if (items == null)
            {
                if (tempItems == null)
                    tempItems = new SoodaObjectToObjectAssociation();
                tempItems[c] = markerAdded;
                return;
            }

            if (items.Contains(c))
                return;

            int pos = itemsArray.Add(c);
            items.Add(c, pos);
        }

        public void InternalRemove(SoodaObject c)
        {
            if (!childType.IsInstanceOfType(c))
                return;

            if (items == null)
            {
                if (tempItems == null)
                    tempItems = new SoodaObjectToObjectAssociation();
                tempItems[c] = markerRemoved;
                return;
            }

            if (!items.Contains(c))
                throw new InvalidOperationException("Attempt to remove object not in collection");

            int pos = items[c];

            SoodaObject lastObj = itemsArray[itemsArray.Count - 1];
            if (lastObj != c)
            {
                itemsArray[pos] = lastObj;
                items[lastObj] = pos;
            }
            itemsArray.RemoveAt(itemsArray.Count - 1);
            items.Remove(c);
        }
{'fraction_non_alphanumeric': 0.07521131619803346, 'fraction_numerical': 0.0022425392444367777, 'mean_word_length': 1.9845559845559846, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3398798', 'n_tokens_mistral': 3033, 'n_tokens_neox': 2828, 'n_words': 930}