<commit_before><?php
// Copyright (C) 2010-2012 Combodo SARL
//
//   This file is part of iTop.
//
//   iTop is free software; you can redistribute it and/or modify	
//   it under the terms of the GNU Affero General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   iTop is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Affero General Public License for more details.
//
//   You should have received a copy of the GNU Affero General Public License
//   along with iTop. If not, see <http://www.gnu.org/licenses/>


/**
 * General definition of an expression tree (could be OQL, SQL or whatever) 
 *
 * @copyright   Copyright (C) 2010-2012 Combodo SARL
 * @license     http://opensource.org/licenses/AGPL-3.0
 */

class MissingQueryArgument extends CoreException
{
}

abstract class Expression
{
	// recursive translation of identifiers
	abstract public function GetUnresolvedFields($sAlias, &$aUnresolved);
	abstract public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);

	// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
	abstract public function Render(&$aArgs = null, $bRetrofitParams = false);

	abstract public function ApplyParameters($aArgs);
	
	// recursively builds an array of class => fieldname
	abstract public function ListRequiredFields();

	// recursively list field parents ($aTable = array of sParent => dummy)
	abstract public function CollectUsedParents(&$aTable);

	abstract public function IsTrue();
	
	// recursively builds an array of [classAlias][fieldName] => value
	abstract public function ListConstantFields();
	
	public function RequiresField($sClass, $sFieldName)
	{
		// #@# todo - optimize : this is called quite often when building a single query !
		$aRequired = $this->ListRequiredFields();
		if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false;
		return true;
	}

	public function serialize()
	{
		return base64_encode($this->Render());
	}
	
	static public function unserialize($sValue)
	{
		return self::FromOQL(base64_decode($sValue));
	}

	static public function FromOQL($sConditionExpr)
	{
		$oOql = new OqlInterpreter($sConditionExpr);
		$oExpression = $oOql->ParseExpression();
		
		return $oExpression;
	}

	static public function FromSQL($sSQL)
	{
		$oSql = new SQLExpression($sSQL);
		return $oSql;
	}

	public function LogAnd($oExpr)
	{
		if ($this->IsTrue()) return clone $oExpr;
		if ($oExpr->IsTrue()) return clone $this;
		return new BinaryExpression($this, 'AND', $oExpr);
	}

	public function LogOr($oExpr)
	{
		return new BinaryExpression($this, 'OR', $oExpr);
	}
	
	abstract public function RenameParam($sOldName, $sNewName);

	/**
	 * Make the most relevant label, given the value of the expression
	 * 	 
	 * @param DBObjectSearch oFilter The context in which this expression has been used	 	
	 * @param string sValue The value returned by the query, for this expression	 	
	 * @param string sDefault The default value if no relevant label could be computed	 	
	 * @return The label
	 */	
	public function MakeValueLabel($oFilter, $sValue, $sDefault)
	{
		return $sDefault;
	}
}

class SQLExpression extends Expression
{
	protected $m_sSQL;

	public function __construct($sSQL)
	{
		$this->m_sSQL  = $sSQL;
	}

	public function IsTrue()
	{
		return false;
	}

	// recursive rendering
	public function Render(&$aArgs = null, $bRetrofitParams = false)
	{
		return $this->m_sSQL;
	}

	public function ApplyParameters($aArgs)
	{
	}
	
	public function GetUnresolvedFields($sAlias, &$aUnresolved)
	{
	}

	public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
	{
		return clone $this;
	}

	public function ListRequiredFields()
	{
		return array();
	}

	public function CollectUsedParents(&$aTable)
	{
	}
	
	public function ListConstantFields()
	{
		return array();
	}
	
	public function RenameParam($sOldName, $sNewName)
	{
		// Do nothing, since there is nothing to rename
	}
}



class BinaryExpression extends Expression
{
	protected $m_oLeftExpr; // filter code or an SQL expression (later?)
	protected $m_oRightExpr;
	protected $m_sOperator;

	public function __construct($oLeftExpr, $sOperator, $oRightExpr)
	{
		if (!is_object($oLeftExpr))
		{
			throw new CoreException('Expecting an Expression object on the left hand', array('found_type' => gettype($oLeftExpr)));
		}
		if (!is_object($oRightExpr))
		{
			throw new CoreException('Expecting an Expression object on the right hand', array('found_type' => gettype($oRightExpr)));
		}
		if (!$oLeftExpr instanceof Expression)
		{
			throw new CoreException('Expecting an Expression object on the left hand', array('found_class' => get_class($oLeftExpr)));
		}
		if (!$oRightExpr instanceof Expression)
		{
			throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
		}
		$this->m_oLeftExpr  = $oLeftExpr;
		$this->m_oRightExpr = $oRightExpr;
		$this->m_sOperator  = $sOperator;
	}

	public function IsTrue()
	{
		// return true if we are certain that it will be true
		if ($this->m_sOperator == 'AND')
		{
			if ($this->m_oLeftExpr->IsTrue() && $this->m_oRightExpr->IsTrue()) return true;
		}
		return false;
	}
	
	public function GetLeftExpr()
	{
		return $this->m_oLeftExpr;
	}

	public function GetRightExpr()
	{
		return $this->m_oRightExpr;
	}

	public function GetOperator()
	{
		return $this->m_sOperator;
	}

	// recursive rendering
	public function Render(&$aArgs = null, $bRetrofitParams = false)
	{
		$sOperator = $this->GetOperator();
		$sLeft = $this->GetLeftExpr()->Render($aArgs, $bRetrofitParams);
		$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
		return "($sLeft $sOperator $sRight)";
	}
	
	public function ApplyParameters($aArgs)
	{
		if ($this->m_oLeftExpr instanceof VariableExpression)
		{
			$this->m_oLeftExpr = $this->m_oLeftExpr->GetAsScalar($aArgs);
		}
		else //if ($this->m_oLeftExpr instanceof Expression)
		{
			$this->m_oLeftExpr->ApplyParameters($aArgs);
		}
		if ($this->m_oRightExpr instanceof VariableExpression)
		{
			$this->m_oRightExpr = $this->m_oRightExpr->GetAsScalar($aArgs);
		}
		else //if ($this->m_oRightExpr instanceof Expression)
		{
			$this->m_oRightExpr->ApplyParameters($aArgs);
		}
	}
	
	public function GetUnresolvedFields($sAlias, &$aUnresolved)
	{
		$this->GetLeftExpr()->GetUnresolvedFields($sAlias, $aUnresolved);
		$this->GetRightExpr()->GetUnresolvedFields($sAlias, $aUnresolved);
	}

	public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
	{
		$oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
		$oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
		return new BinaryExpression($oLeft, $this->GetOperator(), $oRight);
	}

	public function ListRequiredFields()
	{
		$aLeft = $this->GetLeftExpr()->ListRequiredFields();
		$aRight = $this->GetRightExpr()->ListRequiredFields();
		return array_merge($aLeft, $aRight);
	}

	public function CollectUsedParents(&$aTable)
	{
		$this->GetLeftExpr()->CollectUsedParents($aTable);
		$this->GetRightExpr()->CollectUsedParents($aTable);
	}
	
	/**
	 * List all constant expression of the form <field> = <scalar> or <field> = :<variable>
	 * Could be extended to support <field> = <function><constant_expression>
	 */
	public function ListConstantFields()
	{
		$aResult = array();
		if ($this->m_sOperator == '=')
		{
			if (($this->m_oLeftExpr instanceof FieldExpression) && ($this->m_oRightExpr instanceof ScalarExpression))
			{
				$aResult[$this->m_oLeftExpr->GetParent()][$this->m_oLeftExpr->GetName()] = $this->m_oRightExpr;
			}
			else if (($this->m_oRightExpr instanceof FieldExpression) && ($this->m_oLeftExpr instanceof ScalarExpression))
			{
				$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
			}
			else if (($this->m_oLeftExpr instanceof FieldExpression) && ($this->m_oRightExpr instanceof VariableExpression))
			{
				$aResult[$this->m_oLeftExpr->GetParent()][$this->m_oLeftExpr->GetName()] = $this->m_oRightExpr;
			}
			else if (($this->m_oRightExpr instanceof FieldExpression) && ($this->m_oLeftExpr instanceof VariableExpression))
			{
				$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
			}
			else
			{
				$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
			}
		}
		else
		{
			$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
		}
		return $aResult;
	}
	
	public function RenameParam($sOldName, $sNewName)
	{
		$this->GetLeftExpr()->RenameParam($sOldName, $sNewName);
		$this->GetRightExpr()->RenameParam($sOldName, $sNewName);
	}
}


class UnaryExpression extends Expression
{
	protected $m_value;

	public function __construct($value)
	{
		$this->m_value = $value;
	}

	public function IsTrue()
	{
		// return true if we are certain that it will be true
		return ($this->m_value == 1);
	}

	public function GetValue()
	{
		return $this->m_value;
	} 

	// recursive rendering
	public function Render(&$aArgs = null, $bRetrofitParams = false)
	{
		return CMDBSource::Quote($this->m_value);
	}

	public function ApplyParameters($aArgs)
	{
	}
	
	public function GetUnresolvedFields($sAlias, &$aUnresolved)
	{
	}

	public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
	{
		return clone $this;
	}

	public function ListRequiredFields()
	{
		return array();
	}

	public function CollectUsedParents(&$aTable)
	{
	}

	public function ListConstantFields()
	{
		return array();
	}
	
	public function RenameParam($sOldName, $sNewName)
	{
		// Do nothing
		// really ? what about :param{$iParamIndex} ??
	}
}

class ScalarExpression extends UnaryExpression
{
	public function __construct($value)
	{
		if (!is_scalar($value) && !is_null($value) && (!$value instanceof OqlHexValue))
		{
			throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value)));
		}
		parent::__construct($value);
	}

	// recursive rendering
	public function Render(&$aArgs = null, $bRetrofitParams = false)
	{
		if (is_null($this->m_value))
		{
			$sRet = 'NULL';
		}
		else
		{
			$sRet = CMDBSource::Quote($this->m_value);
		}
		return $sRet;
	}
}
<commit_msg>#830 ... completes the change 2980 (that accompanied changes 2921 and 2948, for an optimization on bulk operations)<commit_after><?php
// Copyright (C) 2010-2012 Combodo SARL
//
//   This file is part of iTop.
//
//   iTop is free software; you can redistribute it and/or modify	
//   it under the terms of the GNU Affero General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   iTop is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Affero General Public License for more details.
//
//   You should have received a copy of the GNU Affero General Public License
//   along with iTop. If not, see <http://www.gnu.org/licenses/>


/**
 * General definition of an expression tree (could be OQL, SQL or whatever) 
 *
 * @copyright   Copyright (C) 2010-2012 Combodo SARL
 * @license     http://opensource.org/licenses/AGPL-3.0
 */

class MissingQueryArgument extends CoreException
{
}

abstract class Expression
{
	// recursive translation of identifiers
	abstract public function GetUnresolvedFields($sAlias, &$aUnresolved);
	abstract public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);

	// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
	abstract public function Render(&$aArgs = null, $bRetrofitParams = false);

	abstract public function ApplyParameters($aArgs);
	
	// recursively builds an array of class => fieldname
	abstract public function ListRequiredFields();

	// recursively list field parents ($aTable = array of sParent => dummy)
	abstract public function CollectUsedParents(&$aTable);

	abstract public function IsTrue();
	
	// recursively builds an array of [classAlias][fieldName] => value
	abstract public function ListConstantFields();
	
	public function RequiresField($sClass, $sFieldName)
	{
		// #@# todo - optimize : this is called quite often when building a single query !
		$aRequired = $this->ListRequiredFields();
		if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false;
		return true;
	}

	public function serialize()
	{
		return base64_encode($this->Render());
	}
	
	static public function unserialize($sValue)
	{
		return self::FromOQL(base64_decode($sValue));
	}

	static public function FromOQL($sConditionExpr)
	{
		$oOql = new OqlInterpreter($sConditionExpr);
		$oExpression = $oOql->ParseExpression();
		
		return $oExpression;
	}

	static public function FromSQL($sSQL)
	{
		$oSql = new SQLExpression($sSQL);
		return $oSql;
	}

	public function LogAnd($oExpr)
	{
		if ($this->IsTrue()) return clone $oExpr;
		if ($oExpr->IsTrue()) return clone $this;
		return new BinaryExpression($this, 'AND', $oExpr);
	}

	public function LogOr($oExpr)
	{
		return new BinaryExpression($this, 'OR', $oExpr);
	}
	
	abstract public function RenameParam($sOldName, $sNewName);

	/**
	 * Make the most relevant label, given the value of the expression
	 * 	 
	 * @param DBObjectSearch oFilter The context in which this expression has been used	 	
	 * @param string sValue The value returned by the query, for this expression	 	
	 * @param string sDefault The default value if no relevant label could be computed	 	
	 * @return The label
	 */	
	public function MakeValueLabel($oFilter, $sValue, $sDefault)
	{
		return $sDefault;
	}
}

class SQLExpression extends Expression
{
	protected $m_sSQL;

	public function __construct($sSQL)
	{
		$this->m_sSQL  = $sSQL;
	}

	public function IsTrue()
	{
		return false;
	}

	// recursive rendering
	public function Render(&$aArgs = null, $bRetrofitParams = false)
	{
		return $this->m_sSQL;
	}

	public function ApplyParameters($aArgs)
	{
	}
	
	public function GetUnresolvedFields($sAlias, &$aUnresolved)
	{
	}

	public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
	{
		return clone $this;
	}

	public function ListRequiredFields()
	{
		return array();
	}

	public function CollectUsedParents(&$aTable)
	{
	}
	
	public function ListConstantFields()
	{
		return array();
	}
	
	public function RenameParam($sOldName, $sNewName)
	{
		// Do nothing, since there is nothing to rename
	}
}



class BinaryExpression extends Expression
{
	protected $m_oLeftExpr; // filter code or an SQL expression (later?)
	protected $m_oRightExpr;
	protected $m_sOperator;

	public function __construct($oLeftExpr, $sOperator, $oRightExpr)
	{
		if (!is_object($oLeftExpr))
		{
			throw new CoreException('Expecting an Expression object on the left hand', array('found_type' => gettype($oLeftExpr)));
		}
		if (!is_object($oRightExpr))
		{
			throw new CoreException('Expecting an Expression object on the right hand', array('found_type' => gettype($oRightExpr)));
		}
		if (!$oLeftExpr instanceof Expression)
		{
			throw new CoreException('Expecting an Expression object on the left hand', array('found_class' => get_class($oLeftExpr)));
		}
		if (!$oRightExpr instanceof Expression)
		{
			throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
		}
		$this->m_oLeftExpr  = $oLeftExpr;
		$this->m_oRightExpr = $oRightExpr;
		$this->m_sOperator  = $sOperator;
	}

	public function IsTrue()
	{
		// return true if we are certain that it will be true
		if ($this->m_sOperator == 'AND')
		{
			if ($this->m_oLeftExpr->IsTrue() && $this->m_oRightExpr->IsTrue()) return true;
		}
		return false;
	}
	
	public function GetLeftExpr()
	{
		return $this->m_oLeftExpr;
	}

	public function GetRightExpr()
	{
		return $this->m_oRightExpr;
	}

	public function GetOperator()
	{
		return $this->m_sOperator;
	}

	// recursive rendering
	public function Render(&$aArgs = null, $bRetrofitParams = false)
	{
		$sOperator = $this->GetOperator();
		$sLeft = $this->GetLeftExpr()->Render($aArgs, $bRetrofitParams);
		$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
		return "($sLeft $sOperator $sRight)";
	}
	
	public function ApplyParameters($aArgs)
	{
		if ($this->m_oLeftExpr instanceof VariableExpression)
		{
			$this->m_oLeftExpr = $this->m_oLeftExpr->GetAsScalar($aArgs);
		}
		else //if ($this->m_oLeftExpr instanceof Expression)
		{
			$this->m_oLeftExpr->ApplyParameters($aArgs);
		}
		if ($this->m_oRightExpr instanceof VariableExpression)
		{
			$this->m_oRightExpr = $this->m_oRightExpr->GetAsScalar($aArgs);
		}
		else //if ($this->m_oRightExpr instanceof Expression)
		{
			$this->m_oRightExpr->ApplyParameters($aArgs);
		}
	}
	
	public function GetUnresolvedFields($sAlias, &$aUnresolved)
	{
		$this->GetLeftExpr()->GetUnresolvedFields($sAlias, $aUnresolved);
		$this->GetRightExpr()->GetUnresolvedFields($sAlias, $aUnresolved);
	}

	public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
	{
		$oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
		$oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
		return new BinaryExpression($oLeft, $this->GetOperator(), $oRight);
	}

	public function ListRequiredFields()
	{
		$aLeft = $this->GetLeftExpr()->ListRequiredFields();
		$aRight = $this->GetRightExpr()->ListRequiredFields();
		return array_merge($aLeft, $aRight);
	}

	public function CollectUsedParents(&$aTable)
	{
		$this->GetLeftExpr()->CollectUsedParents($aTable);
		$this->GetRightExpr()->CollectUsedParents($aTable);
	}
	
	/**
	 * List all constant expression of the form <field> = <scalar> or <field> = :<variable>
	 * Could be extended to support <field> = <function><constant_expression>
	 */
	public function ListConstantFields()
	{
		$aResult = array();
		if ($this->m_sOperator == '=')
		{
			if (($this->m_oLeftExpr instanceof FieldExpression) && ($this->m_oRightExpr instanceof ScalarExpression))
			{
				$aResult[$this->m_oLeftExpr->GetParent()][$this->m_oLeftExpr->GetName()] = $this->m_oRightExpr;
			}
			else if (($this->m_oRightExpr instanceof FieldExpression) && ($this->m_oLeftExpr instanceof ScalarExpression))
			{
				$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
			}
			else if (($this->m_oLeftExpr instanceof FieldExpression) && ($this->m_oRightExpr instanceof VariableExpression))
			{
				$aResult[$this->m_oLeftExpr->GetParent()][$this->m_oLeftExpr->GetName()] = $this->m_oRightExpr;
			}
			else if (($this->m_oRightExpr instanceof FieldExpression) && ($this->m_oLeftExpr instanceof VariableExpression))
			{
				$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
			}
			else
			{
				$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
			}
		}
		else
		{
			$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
		}
		return $aResult;
	}
	
	public function RenameParam($sOldName, $sNewName)
	{
		$this->GetLeftExpr()->RenameParam($sOldName, $sNewName);
		$this->GetRightExpr()->RenameParam($sOldName, $sNewName);
	}
}


class UnaryExpression extends Expression
{
	protected $m_value;

	public function __construct($value)
	{
		$this->m_value = $value;
	}

	public function IsTrue()
	{
		// return true if we are certain that it will be true
		return ($this->m_value == 1);
	}

	public function GetValue()
	{
		return $this->m_value;
	} 

	// recursive rendering
	public function Render(&$aArgs = null, $bRetrofitParams = false)
	{
		return CMDBSource::Quote($this->m_value);
	}

	public function ApplyParameters($aArgs)
	{
	}
	
	public function GetUnresolvedFields($sAlias, &$aUnresolved)
	{
	}

	public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
	{
		return clone $this;
	}

	public function ListRequiredFields()
	{
		return array();
	}

	public function CollectUsedParents(&$aTable)
	{
	}

	public function ListConstantFields()
	{
		return array();
	}
	
	public function RenameParam($sOldName, $sNewName)
	{
		// Do nothing
		// really ? what about :param{$iParamIndex} ??
	}
}

class ScalarExpression extends UnaryExpression
{
	public function __construct($value)
	{
		if (!is_scalar($value) && !is_null($value) && (!$value instanceof OqlHexValue))
		{
			throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value)));
		}
		parent::__construct($value);
	}

	// recursive rendering
	public function Render(&$aArgs = null, $bRetrofitParams = false)
	{
		if (is_null($this->m_value))
		{
			$sRet = 'NULL';
		}
		else
		{
			$sRet = CMDBSource::Quote($this->m_value);
		}
		return $sRet;
	}

	public function GetAsScalar($aArgs)
	{
		return clone $this;
	}
}

{'fraction_non_alphanumeric': 0.13113233793934898, 'fraction_numerical': 0.002921264954094408, 'mean_word_length': 4.497578383889881, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 237, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '5873883', 'n_tokens_mistral': 7503, 'n_tokens_neox': 6899, 'n_words': 1746}