@param FinalizedObjectTableDefinition $definition
@param array[]                        $objectProperties
@param ITypedObject[]                 $objects
@param IRowCriteria|null              $criteria

@return array|\Dms\Core\Table\ITableRow[]
973619
private function mapObjectsToRows(
        FinalizedObjectTableDefinition $definition,
        array $objectProperties,
        array $objects,
        IRowCriteria $criteria = null
    ) : array
    {
        $propertyComponentIdMap = $definition->getPropertyComponentIdMap();
        $indexComponentIdMap    = $definition->getIndexComponentIdMap();
        $componentIdCallableMap = $definition->getComponentIdCallableMap();
        $customCallables        = $definition->getCustomCallableMappers();

        if ($criteria) {
            $componentIdsToLoad = [];

            foreach ($criteria->getColumnsToLoad() as $column) {
                foreach ($column->getComponents() as $component) {
                    $componentIdsToLoad[] = $column->getComponentId($component->getName());
                }
            }
            
            $propertyComponentIdMap = array_intersect($propertyComponentIdMap, $componentIdsToLoad);
            $indexComponentIdMap    = array_intersect($indexComponentIdMap, $componentIdsToLoad);
            $componentIdCallableMap = array_intersect_key($componentIdCallableMap, array_flip($componentIdsToLoad));
        }

        $rows = [];

        $componentIdMap = [];

        $allComponentIds = array_merge($propertyComponentIdMap, $indexComponentIdMap, array_keys($componentIdCallableMap));
        foreach ($allComponentIds as $componentId) {
            /** @var IColumn $column */
            /** @var IColumnComponent $component */
            list($column, $component) = $this->structure->getColumnAndComponent($componentId);

            $componentIdMap[$componentId] = [$column->getName(), $component->getName()];
        }

        foreach ($objectProperties as $key => $properties) {
            $row = [];

            foreach ($propertyComponentIdMap as $property => $componentId) {
                list($columnName, $componentName) = $componentIdMap[$componentId];
                $row[$columnName][$componentName] = $properties[$property];
            }

            foreach ($indexComponentIdMap as $componentId) {
                list($columnName, $componentName) = $componentIdMap[$componentId];
                $row[$columnName][$componentName] = $key;
            }

            $object = $objects[$key];

            foreach ($componentIdCallableMap as $componentId => $callable) {
                list($columnName, $componentName) = $componentIdMap[$componentId];
                $row[$columnName][$componentName] = $callable($object);
            }

            foreach ($customCallables as $customCallable) {
                $customCallable($row, $object);
            }

            $rows[] = new TableRowWithObject($row, $object);
        }

        return $rows;
    }
1002305
private function findFieldCriteriaStep2(array &$criteria, ObjectManager $em, ClassMetadata $class, $fieldName): void
    {
        if (null !== $criteria[$fieldName] && $class->hasAssociation($fieldName)) {
            /* Ensure the Proxy is initialized before using reflection to
             * read its identifiers. This is necessary because the wrapped
             * getter methods in the Proxy are being bypassed.
             */
            $em->initializeObject($criteria[$fieldName]);

            $relatedClass = $em->getClassMetadata($class->getAssociationTargetClass($fieldName));
            $relatedId = $relatedClass->getIdentifierValues($criteria[$fieldName]);

            if (\count($relatedId) > 1) {
                throw new ConstraintDefinitionException(
                    'Associated entities are not allowed to have more than one identifier field to be '.
                    'part of a unique constraint in: '.$class->getName().'#'.$fieldName
                );
            }

            $value = array_pop($relatedId);
            $criteria[$fieldName] = Util::getFormattedIdentifier($relatedClass, $criteria, $fieldName, $value);
        }
    }
889310
private Criteria buildPrefetchCriteriaMultipleKeys(Collection ids, FieldDescriptor fields[])
    {
        Criteria crit = new Criteria();
        Iterator iter = ids.iterator();
        Object[] val;
        Identity id;

        while (iter.hasNext())
        {
            Criteria c = new Criteria();
            id = (Identity) iter.next();
            val = id.getPrimaryKeyValues();
            for (int i = 0; i < val.length; i++)
            {
                if (val[i] == null)
                {
                    c.addIsNull(fields[i].getAttributeName());
                }
                else
                {
                    c.addEqualTo(fields[i].getAttributeName(), val[i]);
                }
            }
            crit.addOrCriteria(c);
        }

        return crit;
    }
277808
public function find($criteria = [], array $fields = []) {
		$criteriaNormalized = $this->normalizeCriteria($criteria);
		
		$mongoCursor = $this->getAdapter()->find(
			$this->getTableName(),
			$criteriaNormalized,
			$fields
		);
		
		if ($criteria instanceof Criteria) {
			$this->applyCriteriaToCursor($criteria, $mongoCursor);
		}
		
		return $this->createResultset($mongoCursor);
	}
192475
public function getSearchTags(EntityDefinition $definition, Criteria $criteria): array
    {
        $tags = [$definition->getEntityName() . '.id'];

        foreach ($criteria->getSearchQueryFields() as $accessor) {
            foreach ($this->getFieldsOfAccessor($definition, $accessor) as $association) {
                $tags[] = $association;
            }
        }

        return $tags;
    }
1181621
final private function getObjectCountQuery($properties, $vtable = '?property_values', $objectId = NULL, $objectType = NULL, $objectURI = NULL)
    {

        //Join Query
        $query = "SELECT DISTINCT o.object_id as total_objects";
        $hasProperties = FALSE;
        if (!empty($properties)):
            //Loop through the attributes you need
            $i = 0;
            $count = \sizeof($properties);
            //echo $count;
            foreach ($properties as $alias => $attribute):
                //For count queries, there is no need to have added properties.
                //We will only add does we need in the having clause, which is executed after grouping..
                if (in_array($attribute, $this->listLookUpConditionProperties)):
                    if ($i + 1 < $count):
                        $query .= ",";
                        $i++;
                    endif;
                    $alias = (is_int($alias)) ? $attribute : $alias;
                    $query .= "\nMAX(IF(p.property_name = '{$attribute}', v.value_data, null)) AS {$alias}";
                    $hasProperties = TRUE;
                endif;
            endforeach;

            //The data Joins
            $query .= "\nFROM {$vtable} v";
            $query .= ($hasProperties) ? "\nLEFT JOIN ?properties p ON p.property_id = v.property_id" : NULL;
            $query .= "\nLEFT JOIN ?objects o ON o.object_id=v.object_id";
        else:
            $query .= "\nFROM ?objects o";
        endif;

        static::$withConditions = false;
        if (!empty($objectId) || !empty($objectURI) || !empty($objectType)):
            $query .= "\nWHERE";
            if (!empty($objectType)):
                $query .= "\to.object_type='{$objectType}'";
                static::$withConditions = TRUE;
            endif;
            if (!empty($objectURI)):
                $query .= (static::$withConditions) ? "\t AND" : "";
                $query .= "\to.object_uri='{$objectURI}'";
                static::$withConditions = TRUE;
            endif;
            if (!empty($objectId)):
                $query .= (static::$withConditions) ? "\t AND \t" : "";
                $query .= "\to.object_id='{$objectId}'";
                static::$withConditions = TRUE;
            endif;
        endif;

        return $query;
    }
944195
private function arrayToObject(array $data, FieldsAwareDefinitionInterface $definition)
    {
        $class = null;
        if ($definition instanceof ClassAwareDefinitionInterface) {
            $class = $definition->getClass();
        }

        //normalize data
        foreach ($data as $fieldName => &$value) {
            if (!$definition->hasField($fieldName)) {
                continue;
            }
            $fieldDefinition = $definition->getField($fieldName);
            $value = $this->normalizeValue($value, $fieldDefinition->getType());
        }
        unset($value);

        //instantiate object
        if (class_exists($class)) {
            $object = new $class();
        } else {
            $object = $data;
        }

        //populate object
        foreach ($data as $key => $value) {
            if (!$definition->hasField($key)) {
                continue;
            }
            $fieldDefinition = $definition->getField($key);

            if (\is_array($value) && $this->endpoint->hasType($fieldDefinition->getType())) {
                $childType = $this->endpoint->getType($fieldDefinition->getType());
                if ($childType instanceof FieldsAwareDefinitionInterface) {
                    $value = $this->arrayToObject($value, $childType);
                }
            }

            $this->setObjectValue($object, $fieldDefinition, $value);
        }

        return $object;
    }
698034
public function criteriaAttributes(): array
    {
        // By default, include all public, non-static properties that were defined by a sub class, and certain ones
        // in this class
        /** @noinspection PhpUnhandledExceptionInspection */
        $class = new \ReflectionClass($this);
        $names = [];

        foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
            if (!$property->isStatic()) {
                $dec = $property->getDeclaringClass();
                if (($dec->getName() === self::class || $dec->isSubclassOf(self::class))
                ) {
                    $names[] = $property->getName();
                }
            }
        }

        return $names;
    }
1265095
private function populateObjectItems($object, array $definition)
    {
        $output = clone $object;
        foreach ($definition as $key => $node) {
            if (isset($node['required'])) {
                if ((!$node['required']) && (!isset($output->{$key}))) {
                    $output->{$key} = $node['default'];
                }
            } else {
                if (!isset($output->{$key})) {
                    $output->{$key} = new stdClass();
                }
                $output->{$key} = $this->populateObjectItems($output->{$key}, $node);
            }
        }
        return $output;
    }
1255365
public function criteriaOnly(array $statements, $bindValues = true): array
    {
        $sql = $bindings = [];
        if (isset($statements['criteria']) === false) {
            return compact('sql', 'bindings');
        }

        [$sql, $bindings] = $this->buildCriteria($statements['criteria'], $bindValues);

        return compact('sql', 'bindings');
    }
1239512
public function mapCriteria(IRowCriteria $criteria) : ILoadCriteria
    {
        $objectCriteria = $this->objectSet instanceof IObjectSetWithLoadCriteriaSupport
            ? $this->objectSet->loadCriteria()
            : new LoadCriteria($this->definition->getClass());

        if ($criteria instanceof IObjectRowCriteria) {
            $objectCriteria = $objectCriteria->merge($criteria->getObjectCriteria());
        }

        foreach ($criteria->getColumnsToLoad() as $column) {
            $objectCriteria->loadAll($this->definition->getPropertiesRequiredFor($column->getName()));
        }

        $mapConditions = function (SpecificationDefinition $objectCriteria, ColumnConditionGroup $conditionGroup) {
            foreach ($conditionGroup->getConditions() as $condition) {
                $objectCriteria->where(
                    $this->mapColumnToPropertyName($condition),
                    $condition->getOperator()->getOperator(),
                    $condition->getValue()
                );
            }
        };

        foreach ($criteria->getConditionGroups() as $columnConditionGroup) {
            if ($columnConditionGroup->getConditionMode() === IRowCriteria::CONDITION_MODE_AND) {
                $mapConditions($objectCriteria, $columnConditionGroup);
            } else {
                $objectCriteria->whereAny(function (SpecificationDefinition $match) use ($mapConditions, $columnConditionGroup) {
                    $mapConditions($match, $columnConditionGroup);
                });
            }
        }

        foreach ($criteria->getOrderings() as $ordering) {
            $objectCriteria->orderBy(
                $this->mapColumnToPropertyName($ordering),
                $ordering->getDirection()
            );
        }

        $objectCriteria->skip($criteria->getRowsToSkip())->limit($criteria->getAmountOfRows());

        return $objectCriteria;
    }
48593
protected function mapFinalPropertyToMapping(array $subclassMappings, array $nestedRelations, FinalizedPropertyDefinition $property)
    {
        $propertyName = $property->getName();

        /** @var IRelation $lastRelation */
        $lastRelation = end($nestedRelations);
        if ($lastRelation) {
            $definition = $lastRelation->getMapper()->getDefinition();
        } else {
            $definition = $subclassMappings ? end($subclassMappings)->getDefinition() : $this->rootEntityMapper->getDefinition();
        }

        if (isset($definition->getPropertyColumnMap()[$propertyName])) {
            $columnName                  = $definition->getPropertyColumnMap()[$propertyName];
            $phpToDbPropertyConverterMap = $definition->getPhpToDbPropertyConverterMap();

            return new ColumnMapping(
                $this->rootEntityMapper,
                $subclassMappings,
                $nestedRelations,
                $definition->getTable()->getColumn($columnName),
                isset($phpToDbPropertyConverterMap[$propertyName]) ? $phpToDbPropertyConverterMap[$propertyName] : null
            );
        } elseif ($relation = $definition->getRelationMappedToProperty($propertyName)) {
            return $this->mapFinalRelationToMapping($subclassMappings, $nestedRelations, $relation);
        }

        throw InvalidArgumentException::format(
            'cannot map property \'%s\' of type %s, property is not mapped according to mapper definition for type %s',
            $propertyName, $property->getType()->asTypeString(), $definition->getClassName()
        );
    }
1177313
public function loadAllProperties(LoadingContext $context, array $rows, array $objects): array
    {
        /** @var Row[] $rows */
        foreach ($rows as $key => $row) {
            $rows[$key] = $this->processRowBeforeLoadSubclass($row);
        }

        $definition = $this->definition;

        $propertyMap         = $definition->getPropertyColumnMap();
        $dbToPhpConverterMap = $definition->getDbToPhpPropertyConverterMap();
        $columnSetterMap     = $this->definition->getColumnSetterMap();

        $objectProperties = [];
        $columnDataArray  = [];

        foreach ($rows as $key => $object) {
            $objectProperties[$key] = [];
            $columnDataArray[$key]  = $rows[$key]->getColumnData();
        }

        foreach ($propertyMap as $property => $column) {

            if (isset($dbToPhpConverterMap[$property])) {
                $converter = $dbToPhpConverterMap[$property];
                foreach ($objectProperties as $key => &$properties) {
                    $properties[$property] = $converter($columnDataArray[$key][$column], $columnDataArray[$key]);
                }
            } else {
                foreach ($objectProperties as $key => &$properties) {
                    $properties[$property] = $columnDataArray[$key][$column];
                }
            }
            unset($properties);
        }

        foreach ($objectProperties as $key => $properties) {
            $objects[$key]->hydrate($properties);
            $objectProperties[$key] = [];
        }

        foreach ($columnSetterMap as $column => $setterCallback) {
            foreach ($objects as $key => $object) {
                $setterCallback($object, $rows[$key]->getColumn($column));
            }
        }

        $this->loadRelations($context, $rows, $objects, $objectProperties);

        /** @var IObjectMapping $mapping */
        foreach ($this->subClassMappings as $subClass => $mapping) {
            list($objectPropertiesOfSubClass, $rowsForSubClass) = $this->loadPropertiesForSubclass($mapping, $rows, $objectProperties);

            if (!$objectPropertiesOfSubClass) {
                continue;
            }

            foreach ($mapping->loadAllProperties($context, $rowsForSubClass, $objects) as $key => $subClassProperties) {
                $objectProperties[$key] += $subClassProperties;
            }
        }

        return $objectProperties;
    }
890037
private function expandCriteriaParameters(Criteria $criteria)
    {
        $expression = $criteria->getWhereExpression();

        if ($expression === null) {
            return [];
        }

        $valueVisitor = new SqlValueVisitor();

        $valueVisitor->dispatch($expression);

        [, $types] = $valueVisitor->getParamsAndTypes();

        return $types;
    }
399447
final public function saveObject($objectURI = NULL, $objectType = NULL, $objectId = NULL, $forceNew = false )
    {

        $isNew = (empty($objectId) && empty($objectURI)) ? true : (($forceNew)? true : false );

        //if we are forcing a new object;

        //Get a randomstring for the objectURI
        $objectURI = empty($objectURI) ? getRandomString(10, false, true) : $objectURI;
        $objectType = empty($objectType) ? 'entity' : $objectType;
        //$objectId = empty($objectId) ?  (!empty($this->objectId) ? $this->objectId : null ) : $objectId;
        //Ensure we have all the properties
        if (empty($this->propertyModel) || empty($this->propertyData))
            return false; //We have nothing to save

        //Use a transaction;
        $this->database->startTransaction();
        $pquery = "INSERT IGNORE INTO ?properties (property_name, property_label, property_datatype, property_charsize, property_default, property_indexed) VALUES\n";
        $pqueryV = array();
        foreach ($this->propertyModel as $property => $definition) {
            $values = array($this->database->quote($property), $this->database->quote($definition[0]), $this->database->quote($definition[1])); //Name, Label, DataType
            $values[] = (isset($definition[2])) ? $this->database->quote($definition[2]) : $this->database->quote(""); //Charsize
            $values[] = (isset($definition[3])) ? $this->database->quote($definition[3]) : $this->database->quote(""); //Default
            $values[] = (isset($definition[4]) && $definition[4]) ? $this->database->quote(1) : $this->database->quote(0); //Indexed

            $pqueryV[] = " (" . implode(', ', $values) . " )";
        }
        $pquery .= implode(', ', $pqueryV);


        //update the properties
        $this->database->query($pquery);

        //If objectId is NULL then NEW Create new object
        if ($isNew):

            //Log::message("Object ID is {$objectId}");

            $timestamp = Time::stamp();

            $oquery = $this->database->insert("?objects", array("object_uri" => $this->database->quote($objectURI), "object_type" => $this->database->quote($objectType), "object_created_on" => $this->database->quote($timestamp)), FALSE, NULL, FALSE);
            $this->database->query($oquery);

            $this->setLastSavedObjectURI($objectURI);
        endif;



        //If property exists and value doesnt insert new value row
        //If property exists and value exists update value
        //if property does not exists, insert property and insert value
        $vtable = "?{$this->valueGroup}property_values";

        $iquery = "REPLACE INTO {$vtable} (property_id, object_id, value_data)";
        $iqueryV = array();
        foreach ($this->propertyData as $propertyName => $valueData):
            //@TODO validate the data?
            if (empty($valueData))
                continue; //There is no point in storing empty values;


//@TODO also check that value data has data for fields demarkated as allowempty=false;
            $iqueryV[] = "\nSELECT p.property_id, o.object_id, {$this->database->quote($valueData)}  FROM `?properties` AS p JOIN `?objects` AS o WHERE o.object_uri={$this->database->quote($objectURI)} AND p.property_name={$this->database->quote($propertyName)}";
        endforeach;
        $iquery .= implode("\nUNION ALL", $iqueryV);

        $this->database->query($iquery);
        //Update the object URI so the last update field is auto updated
        //$this->database->exec( "UPDATE ?objects SET objected_updated_on=CURRENT_TIMESTAMP" WHERE object_uri=" );

        if (!$this->database->commitTransaction()) {
            throw new Exception("Could not commit transaction");
            return false;
        }

        return true;
    }
75179
public function getBasicValueForArrayProperty(PropertyDefinition $propertyDefinition, $rawValue)
	{
		if (!$propertyDefinition->getIsArray() || !is_array($rawValue)) {
			return [];
		}

		$values = [];

		foreach ($rawValue as $rawValueItem) {

			if ($propertyDefinition->getType() == PropertyDefinition::BASIC_TYPE_INTEGER) {
				if (is_numeric($rawValueItem) || is_bool($rawValueItem) || is_string($rawValueItem)) {
					$values[] = intval($rawValueItem);
				}

			} else if ($propertyDefinition->getType() == PropertyDefinition::BASIC_TYPE_FLOAT) {
				if (is_numeric($rawValueItem) || is_bool($rawValueItem) || is_string($rawValueItem)) {
					$values[] = floatval($rawValueItem);
				}
			} else if ($propertyDefinition->getType() == PropertyDefinition::BASIC_TYPE_STRING) {
				if (!is_array($rawValueItem) && !is_object($rawValueItem)) {
					$values[] = strval($rawValueItem);
				}
			} else if ($propertyDefinition->getType() == PropertyDefinition::BASIC_TYPE_BOOLEAN) {
				if (!is_array($rawValueItem) && !is_object($rawValueItem)) {
					$values[] = boolval($rawValueItem);
				}
			} else {
				// skip this value
			}
		}

		return $values;
	}
1063967
private function callObjectMethod($object, DoctrineCallback $constraint): void
    {
        $callback = (string) $constraint->callback;

        if (!method_exists($object, $callback)) {
            throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by Callback constraint does not exist', $callback));
        }

        $reflMethod = new \ReflectionMethod($object, $callback);

        if ($reflMethod->isStatic()) {
            $reflMethod->invoke(null, $object, $this->context, $this->registry);
        } else {
            $reflMethod->invoke($object, $this->context, $this->registry);
        }
    }