text:
<?php

/**
 * This is a utility class for holding criteria information for a query.
 *
 * BasePeer constructs SQL statements based on the values in this class.
 *
 * @author     Hans Lellelid <hans@xmpl.org> (Propel)
 * @author     Kaspars Jaudzems <kaspars.jaudzems@inbox.lv> (Propel)
 * @author     Frank Y. Kim <frank.kim@clearink.com> (Torque)
 * @author     John D. McNally <jmcnally@collab.net> (Torque)
 * @author     Brett McLaughlin <bmclaugh@algx.net> (Torque)
 * @author     Eric Dobbs <eric@dobbse.net> (Torque)
 * @author     Henning P. Schmiedehausen <hps@intermeta.de> (Torque)
 * @author     Sam Joseph <sam@neurogrid.com> (Torque)
 * @version    $Revision: 1077 $
 * @package    propel.util
 */
class Criteria implements IteratorAggregate {

	/** Comparison type. */
	const EQUAL = "=";

	/** Comparison type. */
	const NOT_EQUAL = "<>";

	/** Comparison type. */
	const ALT_NOT_EQUAL = "!=";

	/** Comparison type. */
	const GREATER_THAN = ">";

	/** Comparison type. */
	const LESS_THAN = "<";

	/** Comparison type. */
	const GREATER_EQUAL = ">=";

	/** Comparison type. */
	const LESS_EQUAL = "<=";

	/** Comparison type. */
	const LIKE = " LIKE ";

	/** Comparison type. */
	const NOT_LIKE = " NOT LIKE ";

	/** PostgreSQL comparison type */
	const ILIKE = " ILIKE ";

	/** PostgreSQL comparison type */
	const NOT_ILIKE = " NOT ILIKE ";

	/** Comparison type. */
	const CUSTOM = "CUSTOM";

	/** Comparison type for update */
	const CUSTOM_EQUAL = "CUSTOM_EQUAL";

	/** Comparison type. */
	const DISTINCT = "DISTINCT ";

	/** Comparison type. */
	const IN = " IN ";

	/** Comparison type. */
	const NOT_IN = " NOT IN ";

	/** Comparison type. */
	const ALL = "ALL ";

	/** Comparison type. */
	const JOIN = "JOIN";

	/** Binary math operator: AND */
	const BINARY_AND = "&";

	/** Binary math operator: OR */
	const BINARY_OR = "|";

	/** "Order by" qualifier - ascending */
	const ASC = "ASC";

	/** "Order by" qualifier - descending */
	const DESC = "DESC";

	/** "IS NULL" null comparison */
	const ISNULL = " IS NULL ";

	/** "IS NOT NULL" null comparison */
	const ISNOTNULL = " IS NOT NULL ";

	/** "CURRENT_DATE" ANSI SQL function */
	const CURRENT_DATE = "CURRENT_DATE";

	/** "CURRENT_TIME" ANSI SQL function */
	const CURRENT_TIME = "CURRENT_TIME";

	/** "CURRENT_TIMESTAMP" ANSI SQL function */
	const CURRENT_TIMESTAMP = "CURRENT_TIMESTAMP";

	/** "LEFT JOIN" SQL statement */
	const LEFT_JOIN = "LEFT JOIN";

	/** "RIGHT JOIN" SQL statement */
	const RIGHT_JOIN = "RIGHT JOIN";

	/** "INNER JOIN" SQL statement */
	const INNER_JOIN = "INNER JOIN";

	private $ignoreCase = false;
	private $singleRecord = false;
	private $selectModifiers = array();
	private $selectColumns = array();
	private $orderByColumns = array();
	private $groupByColumns = array();
	private $having = null;
	private $asColumns = array();
	private $joins = array();

	/** The name of the database. */
	private $dbName;

	/**
	 * The primary table for this Criteria.
	 * Useful in cases where there are no select or where
	 * columns.
	 * @var        string
	 */
	private $primaryTableName;

	/** The name of the database as given in the contructor. */
	private $originalDbName;

	/**
	 * To limit the number of rows to return.  0 means return all
	 * rows.
	 */
	private $limit = 0;

	/** To start the results at a row other than the first one. */
	private $offset = 0;

	// flag to note that the criteria involves a blob.
	private $blobFlag = null;

	private $aliases = array();

	private $useTransaction = false;

	/**
	 * Primary storage of criteria data.
	 * @var        array
	 */
	private $map = array();

	/**
	 * Creates a new instance with the default capacity which corresponds to
	 * the specified database.
	 *
	 * @param      dbName The dabase name.
	 */
	public function __construct($dbName = null)
	{
		$this->setDbName($dbName);
		$this->originalDbName = $dbName;
	}

	/**
	 * Implementing SPL IteratorAggregate interface.  This allows
	 * you to foreach () over a Criteria object.
	 */
	public function getIterator()
	{
		return new CriterionIterator($this);
	}

	/**
	 * Get the criteria map.
	 * @return     array
	 */
	public function getMap()
	{
		return $this->map;
	}

	/**
	 * Brings this criteria back to its initial state, so that it
	 * can be reused as if it was new. Except if the criteria has grown in
	 * capacity, it is left at the current capacity.
	 * @return     void
	 */
	public function clear()
	{
		$this->map = array();
		$this->ignoreCase = false;
		$this->singleRecord = false;
		$this->selectModifiers = array();
		$this->selectColumns = array();
		$this->orderByColumns = array();
		$this->groupByColumns = array();
		$this->having = null;
		$this->asColumns = array();
		$this->joins = array();
		$this->dbName = $this->originalDbName;
		$this->offset = 0;
		$this->limit = -1;
		$this->blobFlag = null;
		$this->aliases = array();
		$this->useTransaction = false;
	}

	/**
	 * Add an AS clause to the select columns. Usage:
	 *
	 *
	 * Criteria myCrit = new Criteria();
	 * myCrit->addAsColumn("alias", "ALIAS(".MyPeer::ID.")");
	 *
	 *
	 * @param      string $name Wanted Name of the column (alias).
	 * @param      string $clause SQL clause to select from the table
	 *
	 * If the name already exists, it is replaced by the new clause.
	 *
	 * @return     Criteria A modified Criteria object.
	 */
	public function addAsColumn($name, $clause)
	{
		$this->asColumns[$name] = $clause;
		return $this;
	}

	/**
	 * Get the column aliases.
	 *
	 * @return     array An assoc array which map the column alias names
	 * to the alias clauses.
	 */
	public function getAsColumns()
	{
		return $this->asColumns;
	}

		/**
	 * Returns the column name associated with an alias (AS-column).
	 *
	 * @param      string $alias
	 * @return     string $string
	 */
	public function getColumnForAs($as)
	{
		if (isset($this->asColumns[$as])) {
			return $this->asColumns[$as];
		}
	}

	/**
	 * Allows one to specify an alias for a table that can
	 * be used in various parts of the SQL.
	 *
	 * @param      string $alias
	 * @param      string $table
	 * @return     void
	 */
	public function addAlias($alias, $table)
	{
		$this->aliases[$alias] = $table;
	}

	/**
	 * Returns the table name associated with an alias.
	 *
	 * @param      string $alias
	 * @return     string $string
	 */
	public function getTableForAlias($alias)
	{
		if (isset($this->aliases[$alias])) {
			return $this->aliases[$alias];
		}
	}

	/**
	 * Get the keys for the criteria map.
	 * @return     array
	 */
	public function keys()
	{
		return array_keys($this->map);
	}

	/**
	 * Does this Criteria object contain the specified key?
	 *
	 * @param      string $column [table.]column
	 * @return     boolean True if this Criteria object contain the specified key.
	 */
	public function containsKey($column)
	{
		// must use array_key_exists() because the key could
		// exist but have a NULL value (that'd be valid).
		return array_key_exists($column, $this->map);
	}

	/**
	 * Does this Criteria object contain the specified key and does it have a value set for the key
	 *
	 * @param      string $column [table.]column
	 * @return     boolean True if this Criteria object contain the specified key and a value for that key
	 */
	public function keyContainsValue($column)
	{
		// must use array_key_exists() because the key could
		// exist but have a NULL value (that'd be valid).
		return (array_key_exists($column, $this->map) && ($this->map[$column]->getValue() !== null) );
	}

	/**
	 * Will force the sql represented by this criteria to be executed within
	 * a transaction.  This is here primarily to support the oid type in
	 * postgresql.  Though it can be used to require any single sql statement
	 * to use a transaction.
	 * @return     void
	 */
	public function setUseTransaction($v)
	{
		$this->useTransaction = (boolean) $v;
	}

	/**
	 * Whether the sql command specified by this criteria must be wrapped
	 * in a transaction.
	 *
	 * @return     boolean
	 */
	public function isUseTransaction()
	{
		return $this->useTransaction;
	}

	/**
	 * Method to return criteria related to columns in a table.
	 *
	 * @param      string $column Column name.
	 * @return     Criterion A Criterion or null if $column is invalid.
	 */
	public function getCriterion($column)
	{
		if ( isset ( $this->map[$column] ) ) {
			return $this->map[$column];
		}
		return null;
	}

	/**
	 * Method to return criterion that is not added automatically
	 * to this Criteria.  This can be used to chain the
	 * Criterions to form a more complex where clause.
	 *
	 * @param      string $column Full name of column (for example TABLE.COLUMN).
	 * @param      mixed $value
	 * @param      string $comparison
	 * @return     Criterion
	 */
	public function getNewCriterion($column, $value, $comparison = null)
	{
		return new Criterion($this, $column, $value, $comparison);
	}

	/**
	 * Method to return a String table name.
	 *
	 * @param      string $name Name of the key.
	 * @return     string The value of the object at key.
	 */
	public function getColumnName($name)
	{
		if (isset($this->map[$name])) {
			return $this->map[$name]->getColumn();
		}
		return null;
	}

	/**
	 * Shortcut method to get an array of columns indexed by table.
	 * @return     array array(table => array(table.column1, table.column2))
	 */
	public function getTablesColumns()
	{
		$tables = array();
		foreach ( array_keys ( $this->map ) as $key) {
			$t = substr ( $key, 0, strrpos ( $key, '.' ) );
			if ( ! isset ( $tables[$t] ) ) {
				$tables[$t] = array( $key );
			} else {
				$tables[$t][] = $key;
			}
		}
		return $tables;
	}

	/**
	 * Method to return a comparison String.
	 *
	 * @param      string $key String name of the key.
	 * @return     string A String with the value of the object at key.
	 */
	public function getComparison($key)
	{
		if ( isset ( $this->map[$key] ) ) {
			return $this->map[$key]->getComparison();
		}
		return null;
	}

	/**
	 * Get the Database(Map) name.
	 *
	 * @return     string A String with the Database(Map) name.
	 */
	public function getDbName()
	{
		return $this->dbName;
	}

	/**
	 * Set the DatabaseMap name.  If null is supplied, uses value
	 * provided by Propel::getDefaultDB().
	 *
	 * @param      string $dbName The Database (Map) name.
	 * @return     void
	 */
	public function setDbName($dbName = null)
	{
		$this->dbName = ($dbName === null ? Propel::getDefaultDB() : $dbName);
	}

	/**
	 * Get the primary table for this Criteria.
	 *
	 * This is useful for cases where a Criteria may not contain
	 * any SELECT columns or WHERE columns.  This must be explicitly
	 * set, of course, in order to be useful.
	 *
	 * @return     string
	 */
	public function getPrimaryTableName()
	{
		return $this->primaryTableName;
	}

	/**
	 * Sets the primary table for this Criteria.
	 *
	 * This is useful for cases where a Criteria may not contain
	 * any SELECT columns or WHERE columns.  This must be explicitly
	 * set, of course, in order to be useful.
	 *
	 * @param      string $v
	 */
	public function setPrimaryTableName($tableName)
	{
		$this->primaryTableName = $tableName;
	}

	/**
	 * Method to return a String table name.
	 *
	 * @param      string $name The name of the key.
	 * @return     string The value of table for criterion at key.
	 */
	public function getTableName($name)
	{
		if (isset($this->map[$name])) {
			return $this->map[$name]->getTable();
		}
		return null;
	}

	/**
	 * Method to return the value that was added to Criteria.
	 *
	 * @param      string $name A String with the name of the key.
	 * @return     mixed The value of object at key.
	 */
	public function getValue($name)
	{
		if (isset($this->map[$name])) {
			return $this->map[$name]->getValue();
		}
		return null;
	}

	/**
	 * An alias to getValue() -- exposing a Hashtable-like interface.
	 *
	 * @param      string $key An Object.
	 * @return     mixed The value within the Criterion (not the Criterion object).
	 */
	public function get($key)
	{
		return $this->getValue($key);
	}

	/**
	 * Overrides Hashtable put, so that this object is returned
	 * instead of the value previously in the Criteria object.
	 * The reason is so that it more closely matches the behavior
	 * of the add() methods. If you want to get the previous value
	 * then you should first Criteria.get() it yourself. Note, if
	 * you attempt to pass in an Object that is not a String, it will
	 * throw a NPE. The reason for this is that none of the add()
	 * methods support adding anything other than a String as a key.
	 *
	 * @param      string $key
	 * @param      mixed $value
	 * @return     Instance of self.
	 */
	public function put($key, $value)
	{
		return $this->add($key, $value);
	}

	/**
	 * Copies all of the mappings from the specified Map to this Criteria
	 * These mappings will replace any mappings that this Criteria had for any
	 * of the keys currently in the specified Map.
	 *
	 * if the map was another Criteria, its attributes are copied to this
	 * Criteria, overwriting previous settings.
	 *
	 * @param      mixed $t Mappings to be stored in this map.
	 */
	public function putAll($t)
	{

		if (is_array($t)) {

			foreach ($t as $key=>$value) {

				if ($value instanceof Criterion) {

					$this->map[$key] = $value;

				} else {

					$this->put($key, $value);

				}

			}

		} elseif ($t instanceof Criteria) {

			$this->joins = $t->joins;

		}

	}

	/**
	 * This method adds a new criterion to the list of criterias.
	 * If a criterion for the requested column already exists, it is
	 * replaced. If is used as follow:
	 *
	 *

	 *
	 * $crit = new Criteria();
	 * $crit->add("column",
	 *                                      "value"
	 *                                      "Criteria::GREATER_THAN");
	 *
	 *
	 * Any comparison can be used.
	 *
	 * The name of the table must be used implicitly in the column name,
	 * so the Column name must be something like 'TABLE.id'. If you
	 * don't like this, you can use the add(table, column, value) method.
	 *
	 * @param      string $critOrColumn The column to run the comparison on, or Criterion object.
	 * @param      mixed $value
	 * @param      string $comparison A String.
	 *
	 * @return     A modified Criteria object.
	 */
	public function add($p1, $value = null, $comparison = null)
	{
		if ($p1 instanceof Criterion) {
			$this->map[$p1->getTable() . '.' . $p1->getColumn()] = $p1;
		} else {
			$this->map[$p1] = new Criterion($this, $p1, $value, $comparison);
		}
		return $this;
	}

	/**
	 * This is the way that you should add a straight (inner) join of two tables.  For
	 * example:
	 *
	 *

	 * AND PROJECT.PROJECT_ID=FOO.PROJECT_ID
	 *

	 *
	 * left = PROJECT.PROJECT_ID
	 * right = FOO.PROJECT_ID
	 *
	 * @param      string $left A String with the left side of the join.
	 * @param      string $right A String with the right side of the join.
		 * @param      string $operator A String with the join operator e.g. LEFT JOIN, ...
	 * @return     Criteria A modified Criteria object.
	 */
	public function addJoin($left, $right, $operator = null)
	{
		$j = new Join($left, $right, $operator);
		if (!in_array($j, $this->joins)) { // compare equality, NOT identity
			$this->joins[] = $j;
		}
		return $this;
	}

	/**
	 * Get the array of Joins.
	 * @return     array Join[]
	 */
	public function getJoins()
	{
		return $this->joins;
	}

	/**
	 * Adds "ALL" modifier to the SQL statement.
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function setAll()
	{
		$this->selectModifiers[] = self::ALL;
		return $this;
	}

	/**
	 * Adds "DISTINCT" modifier to the SQL statement.
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function setDistinct()
	{
		$this->selectModifiers[] = self::DISTINCT;
		return $this;
	}

	/**
	 * Sets ignore case.
	 *
	 * @param      boolean $b True if case should be ignored.
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function setIgnoreCase($b)
	{
		$this->ignoreCase = (boolean) $b;
		return $this;
	}

	/**
	 * Is ignore case on or off?
	 *
	 * @return     boolean True if case is ignored.
	 */
	public function isIgnoreCase()
	{
		return $this->ignoreCase;
	}

	/**
	 * Set single record?  Set this to true if you expect the query
	 * to result in only a single result record (the default behaviour is to
	 * throw a PropelException if multiple records are returned when the query
	 * is executed).  This should be used in situations where returning multiple
	 * rows would indicate an error of some sort.  If your query might return
	 * multiple records but you are only interested in the first one then you
	 * should be using setLimit(1).
	 *
	 * @param      boolean $b Set to TRUE if you expect the query to select just one record.
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function setSingleRecord($b)
	{
		$this->singleRecord = (boolean) $b;
		return $this;
	}

	/**
	 * Is single record?
	 *
	 * @return     boolean True if a single record is being returned.
	 */
	public function isSingleRecord()
	{
		return $this->singleRecord;
	}

	/**
	 * Set limit.
	 *
	 * @param      limit An int with the value for limit.
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function setLimit($limit)
	{
		// TODO: do we enforce int here? 32bit issue if we do
		$this->limit = $limit;
		return $this;
	}

	/**
	 * Get limit.
	 *
	 * @return     int An int with the value for limit.
	 */
	public function getLimit()
	{
		return $this->limit;
	}

	/**
	 * Set offset.
	 *
	 * @param      int $offset An int with the value for offset.  (Note this values is
	 * 							cast to a 32bit integer and may result in truncatation)
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function setOffset($offset)
	{
		$this->offset = (int) $offset;
		return $this;
	}

	/**
	 * Get offset.
	 *
	 * @return     An int with the value for offset.
	 */
	public function getOffset()
	{
		return $this->offset;
	}

	/**
	 * Add select column.
	 *
	 * @param      string $name Name of the select column.
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function addSelectColumn($name)
	{
		$this->selectColumns[] = $name;
		return $this;
	}

	/**
	 * Whether this Criteria has any select columns.
	 *
	 * This will include columns added with addAsColumn() method.
	 *
	 * @return     boolean
	 * @see        addAsColumn()
	 * @see        addSelectColumn()
	 */
	public function hasSelectClause()
	{
		return (!empty($this->selectColumns) || !empty($this->asColumns));
	}

	/**
	 * Get select columns.
	 *
	 * @return     array An array with the name of the select
	 * columns.
	 */
	public function getSelectColumns()
	{
		return $this->selectColumns;
	}

	/**
	 * Clears current select columns.
	 *
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function clearSelectColumns() {
		$this->selectColumns = $this->asColumns = array();
		return $this;
	}

	/**
	 * Get select modifiers.
	 *
	 * @return     An array with the select modifiers.
	 */
	public function getSelectModifiers()
	{
		return $this->selectModifiers;
	}

	/**
	 * Add group by column name.
	 *
	 * @param      string $groupBy The name of the column to group by.
	 * @return     A modified Criteria object.
	 */
	public function addGroupByColumn($groupBy)
	{
		$this->groupByColumns[] = $groupBy;
		return $this;
	}

	/**
	 * Add order by column name, explicitly specifying ascending.
	 *
	 * @param      name The name of the column to order by.
	 * @return     A modified Criteria object.
	 */
	public function addAscendingOrderByColumn($name)
	{
		$this->orderByColumns[] = $name . ' ' . self::ASC;
		return $this;
	}

	/**
	 * Add order by column name, explicitly specifying descending.
	 *
	 * @param      string $name The name of the column to order by.
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function addDescendingOrderByColumn($name)
	{
		$this->orderByColumns[] = $name . ' ' . self::DESC;
		return $this;
	}

	/**
	 * Get order by columns.
	 *
	 * @return     array An array with the name of the order columns.
	 */
	public function getOrderByColumns()
	{
		return $this->orderByColumns;
	}

	/**
	 * Clear the order-by columns.
	 *
	 * @return     Criteria Modified Criteria object (for fluent API)
	 */
	public function clearOrderByColumns()
	{
		$this->orderByColumns = array();
		return $this;
	}

	/**
	 * Clear the group-by columns.
	 *
	 * @return     Criteria
	 */
	public function clearGroupByColumns()
	{
		$this->groupByColumns = array();
		return $this;
	}

	/**
	 * Get group by columns.
	 *
	 * @return     array
	 */
	public function getGroupByColumns()
	{
		return $this->groupByColumns;
	}

	/**
	 * Get Having Criterion.
	 *
	 * @return     Criterion A Criterion object that is the having clause.
	 */
	public function getHaving()
	{
		return $this->having;
	}

	/**
	 * Remove an object from the criteria.
	 *
	 * @param      string $key A string with the key to be removed.
	 * @return     mixed The removed value.
	 */
	public function remove($key)
	{
		if ( isset ( $this->map[$key] ) ) {
			$removed = $this->map[$key];
			unset ( $this->map[$key] );
			if ( $removed instanceof Criterion ) {
				return $removed->getValue();
			}
			return $removed;
		}
	}

	/**
	 * Build a string representation of the Criteria.
	 *
	 * @return     string A String with the representation of the Criteria.
	 */
	public function toString()
	{

		$sb = "Criteria:";
		try {

			$params = array();
			$sb .= "\nSQL (may not be complete): "
			  . BasePeer::createSelectSql($this, $params);

			$sb .= "\nParams: ";
			$paramstr = array();
			foreach ($params as $param) {
				$paramstr[] = $param['table'] . '.' . $param['column'] . ' => ' . var_export($param['value'], true);
			}
			$sb .= implode(", ", $paramstr);

		} catch (Exception $exc) {
			$sb .= "(Error: " . $exc->getMessage() . ")";
		}

		return $sb;
	}

	/**
	 * Returns the size (count) of this criteria.
	 * @return     int
	 */
	public function size()
	{
		return count($this->map);
	}

	/**
	 * This method checks another Criteria to see if they contain
	 * the same attributes and hashtable entries.
	 * @return     boolean
	 */
	public function equals($crit)
	{
		$isEquiv = false;
		if ($crit === null || !($crit instanceof Criteria)) {
			$isEquiv = false;
		} elseif ($this === $crit) {
			$isEquiv = true;
		} elseif ($this->size() === $crit->size()) {

			// Important: nested criterion objects are checked

			$criteria = $crit; // alias
			if ($this->offset === $criteria->getOffset()
				&& $this->limit === $criteria->getLimit()
				&& $this->ignoreCase === $criteria->isIgnoreCase()
				&& $this->singleRecord === $criteria->isSingleRecord()
				&& $this->dbName === $criteria->getDbName()
				&& $this->selectModifiers === $criteria->getSelectModifiers()
				&& $this->selectColumns === $criteria->getSelectColumns()
				&& $this->orderByColumns === $criteria->getOrderByColumns()
				&& $this->groupByColumns === $criteria->getGroupByColumns()
			   )
			{
				$isEquiv = true;
				foreach ($criteria->keys() as $key) {
					if ($this->containsKey($key)) {
						$a = $this->getCriterion($key);
						$b = $criteria->getCriterion($key);
						if (!$a->equals($b)) {
							$isEquiv = false;
							break;
						}
					} else {
						$isEquiv = false;
						break;
					}
				}
			}
		}
		return $isEquiv;
	}

	/**
	 * This method adds a prepared Criterion object to the Criteria as a having clause.
	 * You can get a new, empty Criterion object with the
	 * getNewCriterion() method.
	 *
	 *

	 *
	 * $crit = new Criteria();
	 * $c = $crit->getNewCriterion(BasePeer::ID, 5, Criteria::LESS_THAN);
	 * $crit->addHaving($c);
	 *
	 *
	 * @param      having A Criterion object
	 *
	 * @return     A modified Criteria object.
	 */
	public function addHaving(Criterion $having)
	{
		$this->having = $having;
		return $this;
	}

	/**
	 * This method adds a new criterion to the list of criterias.
	 * If a criterion for the requested column already exists, it is
	 * "AND"ed to the existing criterion.
	  *
	 * addAnd(column, value, comparison)
	 *
	 * $crit = $orig_crit->addAnd("column",
	 *                                      "value"
	 *                                      "Criterion::GREATER_THAN");
	 *
	 *
	 * addAnd(column, value)
	 *
	 * $crit = $orig_crit->addAnd("column", "value");
	 *
	 *
	 * addAnd(Criterion)
	 *
	 * $crit = new Criteria();
	 * $c = $crit->getNewCriterion(BasePeer::ID, 5, Criteria::LESS_THAN);
	 * $crit->addAnd($c);
	 *
	 *
	 * Any comparison can be used, of course.
	 *
	 *
	 * @return     Criteria A modified Criteria object.
	 */
	public function addAnd($p1, $p2 = null, $p3 = null)
	{
		if ($p3 !== null) {
			// addAnd(column, value, comparison)
			$oc = $this->getCriterion($p1);
			$nc = new Criterion($this, $p1, $p2, $p3);
			if ( $oc === null) {
				$this->map[$p1] = $nc;
			} else {
				$oc->addAnd($nc);
			}
		} elseif ($p2 !== null) {
			// addAnd(column, value)
			$this->addAnd($p1, $p2, self::EQUAL);
		} elseif ($p1 instanceof Criterion) {
			// addAnd(Criterion)
			$oc = $this->getCriterion($p1->getTable() . '.' . $p1->getColumn());
			if ($oc === null) {
				$this->add($p1);
			} else {
				$oc->addAnd($p1);
			}
		} elseif ($p2 === null && $p3 === null) {
			// client has not specified $p3 (comparison)
			// which means Criteria::EQUAL but has also specified $p2 == null
			// which is a valid combination we should handle by creating "IS NULL"
			$this->addAnd($p1, $p2, self::EQUAL);
		}
		return $this;
	}

	/**
	 * This method adds a new criterion to the list of criterias.
	 * If a criterion for the requested column already exists, it is
	 * "OR"ed to the existing criterion.
	 *
	 * Any comparison can be used.
	 *
	 * Supports a number of different signatures:
	 *
	 * addOr(column, value, comparison)
	 *
	 * $crit = $orig_crit->addOr("column",
	 *                                      "value"
	 *                                      "Criterion::GREATER_THAN");
	 *
	 *
	 * addOr(column, value)
	 *
	 * $crit = $orig_crit->addOr("column", "value");
	 *
	 *
	 * addOr(Criterion)
	 *
	 * @return     Criteria A modified Criteria object.
	 */
	public function addOr($p1, $p2 = null, $p3 = null)
	{
		if ($p3 !== null) {
			// addOr(column, value, comparison)
			$nc = new Criterion($this, $p1, $p2, $p3);
			$oc = $this->getCriterion($p1);
			if ($oc === null) {
				$this->map[$p1] = $nc;
			} else {
				$oc->addOr($nc);
			}
		} elseif ($p2 !== null) {
			// addOr(column, value)
			$this->addOr($p1, $p2, self::EQUAL);
		} elseif ($p1 instanceof Criterion) {
			// addOr(Criterion)
			$oc = $this->getCriterion($p1->getTable() . '.' . $p1->getColumn());
			if ($oc === null) {
				$this->add($p1);
			} else {
				$oc->addOr($p1);
			}
		} elseif ($p2 === null && $p3 === null) {
			// client has not specified $p3 (comparison)
			// which means Criteria::EQUAL but has also specified $p2 == null
			// which is a valid combination we should handle by creating "IS NULL"
			$this->addOr($p1, $p2, self::EQUAL);
		}

		return $this;
	}
}

// --------------------------------------------------------------------
// Criterion Iterator class -- allows foreach ($criteria as $criterion)
// --------------------------------------------------------------------

/**
 * Class that implements SPL Iterator interface.  This allows foreach () to
 * be used w/ Criteria objects.  Probably there is no performance advantage
 * to doing it this way, but it makes sense -- and simpler code.
 *
 * @author     Hans Lellelid <hans@xmpl.org>
 * @package    propel.util
 */
class CriterionIterator implements Iterator {

	private $idx = 0;
	private $criteria;
	private $criteriaKeys;
	private $criteriaSize;

	public function __construct(Criteria $criteria) {
		$this->criteria = $criteria;
		$this->criteriaKeys = $criteria->keys();
		$this->criteriaSize = count($this->criteriaKeys);
	}

	public function rewind() {
		$this->idx = 0;
	}

	public function valid() {
		return $this->idx < $this->criteriaSize;
	}

	public function key() {
		return $this->criteriaKeys[$this->idx];
	}

	public function current() {
		return $this->criteria->getCriterion($this->criteriaKeys[$this->idx]);
	}

	public function next() {
		$this->idx++;
	}

}

// --------------------------------------------------------------------
// Criterion "inner" class
// --------------------------------------------------------------------

/**
 * This is an "inner" class that describes an object in the criteria.
 *
 * In Torque this is an inner class of the Criteria class.
 *
 * @author     Hans Lellelid <hans@xmpl.org> (Propel)
 * @package    propel.util
 */
class Criterion  {

	const UND = " AND ";
	const ODER = " OR ";

	/** Value of the CO. */
	private $value;

	/** Comparison value.
	 * @var        SqlEnum
	 */
	private $comparison;

	/** Table name. */
	private $table;

	/** Real table name */
	private $realtable;

	/** Column name. */
	private $column;

	/** flag to ignore case in comparision */
	private $ignoreStringCase = false;

	/**
	 * The DBAdaptor which might be used to get db specific
	 * variations of sql.
	 */
	private $db;

	/**
	 * other connected criteria and their conjunctions.
	 */
	private $clauses = array();
	private $conjunctions = array();

	/** "Parent" Criteria class */
	private $parent;

	/**
	 * Create a new instance.
	 *
	 * @param      Criteria $parent The outer class (this is an "inner" class).
	 * @param      string $column TABLE.COLUMN format.
	 * @param      mixed $value
	 * @param      string $comparison
	 */
	public function __construct(Criteria $outer, $column, $value, $comparison = null)
	{
		$this->value = $value;
		$dotPos = strrpos($column,'.');
		if ($dotPos === false) {
			// no dot => aliased column
			$this->table = null;
			$this->column = $column;
		} else {
			$this->table = substr($column, 0, $dotPos);
			$this->column = substr($column, $dotPos+1, strlen($column));
		}
		$this->comparison = ($comparison === null ? Criteria::EQUAL : $comparison);
		$this->init($outer);
	}

	/**
	* Init some properties with the help of outer class
	* @param      Criteria $criteria The outer class
	*/
	public function init(Criteria $criteria)
	{
		//init $this->db
		try {
			$db = Propel::getDB($criteria->getDbName());
			$this->setDB($db);
		} catch (Exception $e) {
			// we are only doing this to allow easier debugging, so
			// no need to throw up the exception, just make note of it.
			Propel::log("Could not get a DBAdapter, sql may be wrong", Propel::LOG_ERR);
		}

		//init $this->realtable
		$realtable = $criteria->getTableForAlias($this->table);
		if (! strlen ( $realtable ) ) {
			$realtable = $this->table;
		}
		$this->realtable = $realtable;

	}

	/**
	 * Get the column name.
	 *
	 * @return     string A String with the column name.
	 */
	public function getColumn()
	{
		return $this->column;
	}

	/**
	 * Set the table name.
	 *
	 * @param      name A String with the table name.
	 * @return     void
	 */
	public function setTable($name)
	{
		$this->table = $name;
	}

	/**
	 * Get the table name.
	 *
	 * @return     string A String with the table name.
	 */
	public function getTable()
	{
		return $this->table;
	}

	/**
	 * Get the comparison.
	 *
	 * @return     string A String with the comparison.
	 */
	public function getComparison()
	{
		return $this->comparison;
	}

	/**
	 * Get the value.
	 *
	 * @return     mixed An Object with the value.
	 */
	public function getValue()
	{
		return $this->value;
	}

	/**
	 * Get the value of db.
	 * The DBAdapter which might be used to get db specific
	 * variations of sql.
	 * @return     DBAdapter value of db.
	 */
	public function getDB()
	{
		return $this->db;
	}

	/**
	 * Set the value of db.
	 * The DBAdapter might be used to get db specific variations of sql.
	 * @param      DBAdapter $v Value to assign to db.
	 * @return     void
	 */
	public function setDB(DBAdapter $v)
	{
		$this->db = $v;
		foreach ( $this->clauses as $clause ) {
			$clause->setDB($v);
		}
	}

	/**
	 * Sets ignore case.
	 *
	 * @param      boolean $b True if case should be ignored.
	 * @return     Criterion A modified Criterion object.
	 */
	public function setIgnoreCase($b)
	{
		$this->ignoreStringCase = (boolean) $b;
		return $this;
	}

	/**
	 * Is ignore case on or off?
	 *
	 * @return     boolean True if case is ignored.
	 */
	 public function isIgnoreCase()
	 {
		 return $this->ignoreStringCase;
	 }

	/**
	 * Get the list of clauses in this Criterion.
	 * @return     array
	 */
	private function getClauses()
	{
		return $this->clauses;
	}

	/**
	 * Get the list of conjunctions in this Criterion
	 * @return     array
	 */
	public function getConjunctions()
	{
		return $this->conjunctions;
	}

	/**
	 * Append an AND Criterion onto this Criterion's list.
	 */
	public function addAnd(Criterion $criterion)
	{
		$this->clauses[] = $criterion;
		$this->conjunctions[] = self::UND;
		return $this;
	}

	/**
	 * Append an OR Criterion onto this Criterion's list.
	 * @return     Criterion
	 */
	public function addOr(Criterion $criterion)
	{
		$this->clauses[] = $criterion;
		$this->conjunctions[] = self::ODER;
		return $this;
	}

	/**
	 * Appends a Prepared Statement representation of the Criterion
	 * onto the buffer.
	 *
	 * @param      string &$sb The string that will receive the Prepared Statement
	 * @param      array $params A list to which Prepared Statement parameters
	 * will be appended
	 * @return     void
	 * @throws     PropelException - if the expression builder cannot figure out how to turn a specified
	 *                           expression into proper SQL.
	 */
	public function appendPsTo(&$sb, array &$params)
	{
		if ($this->column === null) {
			return;
		}

		$db = $this->getDb();
		$sb .= str_repeat ( '(', count($this->clauses) );

		if (Criteria::CUSTOM === $this->comparison) {
			if ($this->value !== "") {
				$sb .= (string) $this->value;
			}
		} else {

			if  ($this->table === null) {
				$field = $this->column;
			} else {
				$field = $this->table . '.' . $this->column;
			}

			// Check to see if table is an alias & store real name, if so
			// (real table name is needed for the returned $params array)
			$realtable = $this->realtable;

			// There are several different types of expressions that need individual handling:
			// IN/NOT IN, LIKE/NOT LIKE, and traditional expressions.

			// OPTION 1:  table.column IN (?, ?) or table.column NOT IN (?, ?)
			if ($this->comparison === Criteria::IN || $this->comparison === Criteria::NOT_IN) {

				$_bindParams = array(); // the param names used in query building
				$_idxstart = count($params);
				$valuesLength = 0;
				foreach ( (array) $this->value as $value ) {
					$valuesLength++; // increment this first to correct for wanting bind params to start with :p1
					$params[] = array('table' => $realtable, 'column' => $this->column, 'value' => $value);
					$_bindParams[] = ':p'.($_idxstart + $valuesLength);
				}
				if ( $valuesLength !== 0 ) {
					$sb .= $field . $this->comparison . '(' . implode(',', $_bindParams) . ')';
				} else {
					$sb .= ($this->comparison === Criteria::IN) ? "1<>1" : "1=1";
				}
				unset ( $value, $valuesLength );

			// OPTION 2:  table.column LIKE ? or table.column NOT LIKE ?  (or ILIKE for Postgres)
			} elseif ($this->comparison === Criteria::LIKE || $this->comparison === Criteria::NOT_LIKE
				|| $this->comparison === Criteria::ILIKE || $this->comparison === Criteria::NOT_ILIKE) {
				// Handle LIKE, NOT LIKE (and related ILIKE, NOT ILIKE for Postgres)

				// If selection is case insensitive use ILIKE for PostgreSQL or SQL
				// UPPER() function on column name for other databases.
				if ($this->ignoreStringCase) {
					if ($db instanceof DBPostgres) {
						if ($this->comparison === Criteria::LIKE) {
							$this->comparison = Criteria::ILIKE;
						} elseif ($this->comparison === Criteria::NOT_LIKE) {
							$this->comparison = Criteria::NOT_ILIKE;
						}
					} else {
						$field = $db->ignoreCase($field);
					}
				}

				$params[] = array('table' => $realtable, 'column' => $this->column, 'value' => $this->value);

				$sb .= $field . $this->comparison;

				// If selection is case insensitive use SQL UPPER() function
				// on criteria or, if Postgres we are using ILIKE, so not necessary.
				if ($this->ignoreStringCase && !($db instanceof DBPostgres)) {
					$sb .= $db->ignoreCase(':p'.count($params));
				} else {
					$sb .= ':p'.count($params);
				}

			// OPTION 3:  table.column = ? or table.column >= ? etc. (traditional expressions, the default)
			} else {

				// NULL VALUES need special treatment because the SQL syntax is different
				// i.e. table.column IS NULL rather than table.column = null
				if ($this->value !== null) {

					// ANSI SQL functions get inserted right into SQL (not escaped, etc.)
					if ($this->value === Criteria::CURRENT_DATE || $this->value === Criteria::CURRENT_TIME || $this->value === Criteria::CURRENT_TIMESTAMP) {
						$sb .= $field . $this->comparison . $this->value;
					} else {

						$params[] = array('table' => $realtable, 'column' => $this->column, 'value' => $this->value);

						// default case, it is a normal col = value expression; value
						// will be replaced w/ '?' and will be inserted later using PDO bindValue()
						if ($this->ignoreStringCase) {
							$sb .= $db->ignoreCase($field) . $this->comparison . $db->ignoreCase(':p'.count($params));
						} else {
							$sb .= $field . $this->comparison . ':p'.count($params);
						}

					}
				} else {

					// value is null, which means it was either not specified or specifically
					// set to null.
					if ($this->comparison === Criteria::EQUAL || $this->comparison === Criteria::ISNULL) {
						$sb .= $field . Criteria::ISNULL;
					} elseif ($this->comparison === Criteria::NOT_EQUAL || $this->comparison === Criteria::ISNOTNULL) {
						$sb .= $field . Criteria::ISNOTNULL;
					} else {
						// for now throw an exception, because not sure how to interpret this
						throw new PropelException("Could not build SQL for expression: $field " . $this->comparison . " NULL");
					}

				}

			}
		}

		foreach ( $this->clauses as $key=>$clause ) {
			$sb .= $this->conjunctions[$key];
			$clause->appendPsTo($sb, $params);
			$sb .= ')';
		}
	}

	/**
	 * This method checks another Criteria to see if they contain
	 * the same attributes and hashtable entries.
	 * @return     boolean
	 */
	public function equals($obj)
	{
		// TODO: optimize me with early outs
		if ($this === $obj) {
			return true;
		}

		if (($obj === null) || !($obj instanceof Criterion)) {
			return false;
		}

		$crit = $obj;

		$isEquiv = ( ( ($this->table === null && $crit->getTable() === null)
			|| ( $this->table !== null && $this->table === $crit->getTable() )
						  )
			&& $this->column === $crit->getColumn()
			&& $this->comparison === $crit->getComparison());

		// check chained criterion

		$clausesLength = count($this->clauses);
		$isEquiv &= (count($crit->getClauses()) == $clausesLength);
		$critConjunctions = $crit->getConjunctions();
		$critClauses = $crit->getClauses();
		for ($i=0; $i < $clausesLength && $isEquiv; $i++) {
			$isEquiv &= ($this->conjunctions[$i] === $critConjunctions[$i]);
			$isEquiv &= ($this->clauses[$i] === $critClauses[$i]);
		}

		if ($isEquiv) {
			$isEquiv &= $this->value === $crit->getValue();
		}

		return $isEquiv;
	}

	/**
	 * Returns a hash code value for the object.
	 */
	public function hashCode()
	{
		$h = crc32(serialize($this->value)) ^ crc32($this->comparison);

		if ($this->table !== null) {
			$h ^= crc32($this->table);
		}

		if ($this->column !== null) {
			$h ^= crc32($this->column);
		}

		foreach ( $this->clauses as $clause ) {
			// TODO: i KNOW there is a php incompatibility with the following line
			// but i dont remember what it is, someone care to look it up and
			// replace it if it doesnt bother us?
			// $clause->appendPsTo($sb='',$params=array());
			$sb = '';
			$params = array();
			$clause->appendPsTo($sb,$params);
			$h ^= crc32(serialize(array($sb,$params)));
			unset ( $sb, $params );
		}

		return $h;
	}

	/**
	 * Get all tables from nested criterion objects
	 * @return     array
	 */
	public function getAllTables()
	{
		$tables = array();
		$this->addCriterionTable($this, $tables);
		return $tables;
	}

	/**
	 * method supporting recursion through all criterions to give
	 * us a string array of tables from each criterion
	 * @return     void
	 */
	private function addCriterionTable(Criterion $c, array &$s)
	{
		$s[] = $c->getTable();
		foreach ( $c->getClauses() as $clause ) {
			$this->addCriterionTable($clause, $s);
		}
	}

	/**
	 * get an array of all criterion attached to this
	 * recursing through all sub criterion
	 * @return     array Criterion[]
	 */
	public function getAttachedCriterion()
	{
		$crits = array();
		$this->traverseCriterion($this, $crits);
		return $crits;
	}

	/**
	 * method supporting recursion through all criterions to give
	 * us an array of them
	 * @param      Criterion $c
	 * @param      array &$a
	 * @return     void
	 */
	private function traverseCriterion(Criterion $c, array &$a)
	{
		$a[] = $c;
		foreach ( $c->getClauses() as $clause ) {
			$this->traverseCriterion($clause, $a);
		}
	}
}

/**
* Data object to describe a join between two tables, for example
*

* table_a LEFT JOIN table_b ON table_a.id = table_b.a_id
*

*/
class Join
{
	/** the left column of the join condition */
	private $leftColumn = null;

	/** the right column of the join condition */
	private $rightColumn = null;

	/** the type of the join (LEFT JOIN, ...), or null */
	private $joinType = null;

	/**
	 * Constructor
	 * @param      leftColumn the left column of the join condition;
	 *        might contain an alias name
	 * @param      rightColumn the right column of the join condition
	 *        might contain an alias name
	 * @param      joinType the type of the join. Valid join types are
	 *        null (adding the join condition to the where clause),
	 *        Criteria::LEFT_JOIN(), Criteria::RIGHT_JOIN(), and Criteria::INNER_JOIN()
	 */
	public function __construct($leftColumn, $rightColumn, $joinType = null)
	{
		if (!is_array($leftColumn) ) {
			$leftColumn = array($leftColumn);
		}
		if (!is_array($rightColumn) ) {
			$rightColumn = array($rightColumn);
		}
		if (count($leftColumn) != count($rightColumn) ) {
			throw new PropelException("Unable to create join because the left column count isn't equal to the right column count");
		}
		$this->leftColumn = $leftColumn;
		$this->rightColumn = $rightColumn;
		$this->joinType = $joinType;
	}

	/**
	 * @return     the type of the join, i.e. Criteria::LEFT_JOIN(), ...,
	 *         or null for adding the join condition to the where Clause
	 */
	public function getJoinType()
	{
		return $this->joinType;
	}

	/**
	 * @return     the left column of the join condition
	 */
	public function getLeftColumn($index = 0)
	{
		return $this->leftColumn[$index];
	}

	/**
	 * @return     all right columns of the join condition
	 */
	public function getLeftColumns() {
		return $this->leftColumn;
	}

	public function getLeftColumnName($index = 0)
	{
		return substr($this->leftColumn[$index], strrpos($this->leftColumn[$index], '.') + 1);
	}

	public function getLeftTableName($index = 0)
	{
		return substr($this->leftColumn[$index], 0, strrpos($this->leftColumn[$index], '.'));
	}

	/**
	 * @return     the right column of the join condition
	 */
	public function getRightColumn($index = 0)
	{
		return $this->rightColumn[$index];
	}

	/**
	 * @return     all right columns of the join condition
	 */
	public function getRightColumns() {
		return $this->rightColumn;
	}

	public function getRightColumnName($index = 0)
	{
		return substr($this->rightColumn[$index], strrpos($this->rightColumn[$index], '.') + 1);
	}

	public function getRightTableName($index = 0)
	{
		return substr($this->rightColumn[$index], 0, strrpos($this->rightColumn[$index], '.'));
	}

	/**
	 * returns a String representation of the class,
	 * mainly for debugging purposes
	 * @return     a String representation of the class
	 */
	public function toString()
	{
		$result = "";
		if ($this->joinType !== null)
		{
			$result .= $this->joinType . " : ";
		}
		$result .= $this->leftColumn . "=" . $this->rightColumn . " (ignoreCase not considered)";

		return $result;
	}
}


text:
// Copyright (c) 2017-2022 The Particl Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef PARTICL_WALLET_HDWALLET_H
#define PARTICL_WALLET_HDWALLET_H

#include <wallet/wallet.h>
#include <wallet/hdwalletdb.h>
#include <wallet/hdwallettypes.h>
#include <wallet/spend.h>
#include <wallet/receive.h>

#include <key_io.h>
#include <key/extkey.h>
#include <key/stealth.h>

using namespace wallet;

static const size_t DEFAULT_STEALTH_LOOKAHEAD_SIZE = 5;

//! -fallbackfee default
static const CAmount DEFAULT_FALLBACK_FEE_PART = 20000;

typedef std::map<CKeyID, CStealthKeyMetadata> StealthKeyMetaMap;
typedef std::map<CKeyID, CExtKeyAccount*> ExtKeyAccountMap;
typedef std::map<CKeyID, CStoredExtKey*> ExtKeyMap;

typedef std::unordered_map<uint256, CWalletTx, SaltedTxidHasher> MapWallet_t;

class UniValue;
typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space;

namespace node {
struct CBlockTemplate;
} // namespace node

class TxValidationState;

class CHDWallet : public wallet::CWallet
{
public:
    CHDWallet(interfaces::Chain* chain, const std::string& name, const ArgsManager& args, std::unique_ptr<WalletDatabase> database) : CWallet(chain, name, args, std::move(database))
    {
        m_default_address_type = OutputType::LEGACY; // In Particl segwit is enabled for all types
        m_fallback_fee = CFeeRate(DEFAULT_FALLBACK_FEE_PART);
    }

    virtual ~CHDWallet()
    {
        Finalise();
    }

    bool IsParticlWallet() const override { return true; };

    int Finalise();
    int FreeExtKeyMaps();

    static void AddOptions(ArgsManager& argsman);

    bool ShouldRescan() override;

    const uint256 &LastBlockProcessed() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
    {
        return m_last_block_processed;
    };

    bool ProcessStakingSettings(std::string &sError);
    bool ProcessWalletSettings(std::string &sError);

    /** Returns true if wallet is initialised */
    bool IsInitialised() const override;

    /** Returns true if HD is enabled */
    bool IsHDEnabled() const override;

    /** Returns true if the wallet can give out new addresses. This means it has keys in the keypool or can generate new keys */
    bool CanGetAddresses(bool internal = false) const override;

    /** Returns true if the wallet's default account requires a hardware device to sign */
    bool IsHardwareLinkedWallet() const;

    /** Unsets a single wallet flag, returns false on fail */
    bool UnsetWalletFlagRV(CHDWalletDB *pwdb, uint64_t flag);

    bool DumpJson(UniValue &rv, std::string &sError);
    bool LoadJson(const UniValue &inj, std::string &sError);

    bool LoadAddressBook(CHDWalletDB *pwdb);

    bool LoadVoteTokens(CHDWalletDB *pwdb) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetVote(int nHeight, uint32_t &token);

    bool LoadTxRecords(CHDWalletDB *pwdb);

    bool IsLocked() const override;
    bool EncryptWallet(const SecureString &strWalletPassphrase) override;
    bool Lock() override;
    using CWallet::Unlock;
    bool Unlock(const SecureString &strWalletPassphrase, bool accept_no_keys = false) override;
    size_t CountKeys() const;

    isminetype HaveAddress(const CTxDestination &dest);
    isminetype HaveKey(const CKeyID &address, const CEKAKey *&pak, const CEKASCKey *&pasc, CExtKeyAccount *&pa) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    isminetype IsMine(const CKeyID &address) const override;
    bool HaveKey(const CKeyID &address) const override;

    isminetype HaveExtKey(const CKeyID &keyID) const;
    bool GetExtKey(const CKeyID &keyID, CStoredExtKey &extKeyOut) const;

    bool HaveTransaction(const uint256 &txhash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetTransaction(const uint256 &txhash, CTransactionRef &tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int GetKey(const CKeyID &address, CKey &keyOut, CExtKeyAccount *&pa, CEKAKey &ak, CKeyID &idStealth) const;
    bool GetKey(const CKeyID &address, CKey &keyOut) const override;
    bool GetPubKey(const CKeyID &address, CPubKey &pkOut) const override;
    bool GetKeyFromPool(CPubKey &key, bool internal = false) override;

    isminetype HaveStealthAddress(const CStealthAddress &sxAddr) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    isminetype IsMine(const CStealthAddress &sxAddr, const CExtKeyAccount *&pa, const CEKAStealthKey *&pask) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetStealthAddressScanKey(CStealthAddress &sxAddr) const;  // Sets scan_secret in sxAddr
    bool GetStealthAddressSpendKey(const CStealthAddress &sxAddr, CKey &key) const;

    bool ImportStealthAddress(const CStealthAddress &sxAddr, const CKey &skSpend);

    DBErrors LoadWallet() override;
    void Downgrade();

    bool AddressBookChangedNotify(const CTxDestination &address, ChangeType nMode);
    bool SetAddressBook(CHDWalletDB *pwdb, const CTxDestination &address, const std::string &strName,
        const std::string &purpose, const std::vector<uint32_t> &vPath, bool fNotifyChanged=true, bool fBech32=false);
    bool SetAddressBook(const CTxDestination &address, const std::string &strName, const std::string &strPurpose, bool fBech32=false) override;
    bool DelAddressBook(const CTxDestination &address) override;

    int64_t GetOldestActiveAccountTime() const;
    int64_t CountActiveAccountKeys() const;

    std::set< std::set<CTxDestination> > GetAddressGroupings() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    std::map<CTxDestination, CAmount> GetAddressBalances() const;

    using CWallet::IsMine;
    isminetype IsMine(const CScript &scriptPubKey, CKeyID &keyID,
        const CEKAKey *&pak, const CEKASCKey *&pasc, CExtKeyAccount *&pa, bool &isInvalid, SigVersion = SigVersion::BASE) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    isminetype IsMineP2SH(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    isminetype IsMine(const CTxOutBase *txout) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool IsMine(const CTransaction& tx) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool IsFromMe(const CTransaction& tx) const override;

    /**
     * Returns amount of debit if the input matches the
     * filter, otherwise returns 0
     */
    CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const override;
    CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const override;
    CAmount GetDebit(const CTransactionRecord &rtx, const isminefilter& filter) const;

    /** Returns whether all of the inputs match the filter */
    bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;

    CAmount GetCredit(const CTxOutBase *txout, const isminefilter &filter) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    void GetCredit(const CTransaction &tx, CAmount &nSpendable, CAmount &nWatchOnly) const;

    CAmount GetOutputValue(const COutPoint &op, bool fAllowTXIndex) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    CAmount GetOwnedOutputValue(const COutPoint &op, isminefilter filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int GetDepthInMainChain(const CTransactionRecord &rtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool InMempool(const uint256 &hash) const;
    bool IsTrusted(const uint256 &txhash, const CTransactionRecord &rtx, int *depth_out = nullptr) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    CAmount GetSpendableBalance() const; // Includes watch_only_cs balance
    CAmount GetBlindBalance();
    CAmount GetAnonBalance();
    CAmount GetStaked();

    bool GetBalances(CHDWalletBalances &bal, bool avoid_reuse = true) const;
    CAmount GetAvailableAnonBalance(const CCoinControl* coinControl = nullptr) const;
    CAmount GetAvailableBlindBalance(const CCoinControl* coinControl = nullptr) const;

    bool IsChange(const CTxOutBase *txout) const override;

    bool GetChangePath(const CScript &script_pubkey, std::vector<uint32_t> &change_path) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int GetChangeAddress(CPubKey &pk);

    void AddOutputRecordMetaData(CTransactionRecord &rtx, std::vector<CTempRecipient> &vecSend);
    int ExpandTempRecipients(std::vector<CTempRecipient> &vecSend, CStoredExtKey *pc, std::string &sError);

    int AddCTData(const CCoinControl *coinControl, CTxOutBase *txout, CTempRecipient &r, std::string &sError) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    bool SetChangeDest(const CCoinControl *coinControl, CTempRecipient &r, std::string &sError) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    /** Update wallet after successful transaction */
    int PostProcessTempRecipients(std::vector<CTempRecipient> &vecSend);

    int AddStandardInputs(CWalletTx &wtx, CTransactionRecord &rtx,
        std::vector<CTempRecipient> &vecSend,
        CExtKeyAccount *sea, CStoredExtKey *pc,
        bool sign, CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);
    int AddStandardInputs(CWalletTx &wtx, CTransactionRecord &rtx,
        std::vector<CTempRecipient> &vecSend, bool sign, CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);

    int AddBlindedInputs(CWalletTx &wtx, CTransactionRecord &rtx,
        std::vector<CTempRecipient> &vecSend,
        CExtKeyAccount *sea, CStoredExtKey *pc,
        bool sign, CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);
    int AddBlindedInputs(CWalletTx &wtx, CTransactionRecord &rtx,
        std::vector<CTempRecipient> &vecSend, bool sign, CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);

    int PlaceRealOutputs(std::vector<std::vector<int64_t> > &vMI, size_t &nSecretColumn, size_t nRingSize, std::set<int64_t> &setHave,
        const std::vector<std::pair<MapRecords_t::const_iterator,unsigned int> > &vCoins, std::vector<uint8_t> &vInputBlinds, const CCoinControl *coinControl, std::string &sError);
    int PickHidingOutputs(std::vector<std::vector<int64_t> > &vMI, size_t nSecretColumn, size_t nRingSize, std::set<int64_t> &setHave, const CCoinControl *coinControl, std::string &sError);

    int AddAnonInputs(CWalletTx &wtx, CTransactionRecord &rtx,
        std::vector<CTempRecipient> &vecSend,
        CExtKeyAccount *sea, CStoredExtKey *pc,
        bool sign, size_t nRingSize, size_t nInputsPerSig, CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);
    int AddAnonInputs(CWalletTx &wtx, CTransactionRecord &rtx,
        std::vector<CTempRecipient> &vecSend, bool sign, size_t nRingSize, size_t nInputsPerSig, CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);

    void ClearCachedBalances() override;
    bool LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    void LoadToWallet(const uint256 &hash, CTransactionRecord &rtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    void leavingIBD() override;

    /** Remove txn from mapwallet and TxSpends */
    void RemoveFromTxSpends(const uint256 &hash, const CTransactionRef pt) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int UnloadTransaction(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int GetDefaultConfidentialChain(CHDWalletDB *pwdb, CExtKeyAccount *&sea, CStoredExtKey *&pc);

    int MakeDefaultAccount();

    int ExtKeyNew32(CExtKey &out);
    int ExtKeyNew32(CExtKey &out, const char *sPassPhrase, int32_t nHash, const char *sSeed);
    int ExtKeyNew32(CExtKey &out, uint8_t *data, uint32_t lenData);

    int ExtKeyImportLoose(CHDWalletDB *pwdb, CStoredExtKey &sekIn, CKeyID &idDerived, bool fBip44, bool fSaveBip44) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyImportAccount(CHDWalletDB *pwdb, CStoredExtKey &sekIn, int64_t nCreatedAt, const std::string &sLabel) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    /** Set master to existing key, remove master key tag from old key if exists */
    int ExtKeySetMaster(CHDWalletDB *pwdb, CKeyID &idMaster) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    /** make and save new root key to wallet */
    int ExtKeyNewMaster(CHDWalletDB *pwdb, CKeyID &idMaster, bool fAutoGenerated = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int ExtKeyCreateAccount(CStoredExtKey *ekAccount, CKeyID &idMaster, CExtKeyAccount &ekaOut, const std::string &sLabel) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    /** Derive a new account from the master key and save to wallet */
    int ExtKeyDeriveNewAccount(CHDWalletDB *pwdb, CExtKeyAccount *sea, const std::string &sLabel, const std::string &sPath="") EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeySetDefaultAccount(CHDWalletDB *pwdb, CKeyID &idNewDefault) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int ExtKeyEncrypt(CStoredExtKey *sek, const CKeyingMaterial &vMKey, bool fLockKey);
    int ExtKeyEncrypt(CExtKeyAccount *sea, const CKeyingMaterial &vMKey, bool fLockKey);
    int ExtKeyEncryptAll(CHDWalletDB *pwdb, const CKeyingMaterial &vMKey);
    int ExtKeyLock();

    int ExtKeyUnlock(CExtKeyAccount *sea) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyUnlock(CExtKeyAccount *sea, const CKeyingMaterial &vMKey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyUnlock(CStoredExtKey *sek) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyUnlock(CStoredExtKey *sek, const CKeyingMaterial &vMKey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyUnlock(const CKeyingMaterial &vMKey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int ExtKeyLoadMaster();

    int ExtKeyLoadAccountKeys(CHDWalletDB *pwdb, CExtKeyAccount *sea) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyLoadAccount(CHDWalletDB *pwdb, const CKeyID &idAccount) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyLoadAccounts();
    /** Load loose extkeys to memory */
    int ExtKeyLoadLoose();
    /** Update in-memory loose extkey */
    int ExtKeyReload(const CStoredExtKey *sek) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    /** Prepare loose extkey lookahead
     *  fake const for IsMine */
    int ExtKeyAddLookAhead(CStoredExtKey *sek) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    /** Promote loose extkey lookahead key to saved key
     *  fake const for IsMine */
    int ExtKeyPromoteKey(CStoredExtKey *sek, uint32_t nChildKey) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int ExtKeySaveAccountToDB(CHDWalletDB *pwdb, const CKeyID &idAccount, CExtKeyAccount *sea) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    /** Activate account in memory
     *  add to mapExtAccounts and mapExtKeys
     */
    int ExtKeyAddAccountToMaps(const CKeyID &idAccount, CExtKeyAccount *sea, bool fAddToLookAhead = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyRemoveAccountFromMapsAndFree(CExtKeyAccount *sea) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyRemoveAccountFromMapsAndFree(const CKeyID &idAccount) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyLoadAccountPacks();
    int PrepareLookahead();

    int ExtKeyAppendToPack(CHDWalletDB *pwdb, CExtKeyAccount *sea, const CKeyID &idKey, const CEKAKey &ak, bool &fUpdateAcc) const;
    int ExtKeyAppendToPack(CHDWalletDB *pwdb, CExtKeyAccount *sea, const CKeyID &idKey, const CEKASCKey &asck, bool &fUpdateAcc) const;

    int ExtKeySaveKey(CHDWalletDB *pwdb, CExtKeyAccount *sea, const CKeyID &keyId, const CEKAKey &ak) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeySaveKey(CExtKeyAccount *sea, const CKeyID &keyId, const CEKAKey &ak) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int ExtKeySaveKey(CHDWalletDB *pwdb, CExtKeyAccount *sea, const CKeyID &keyId, const CEKASCKey &asck) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeySaveKey(CExtKeyAccount *sea, const CKeyID &keyId, const CEKASCKey &asck) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int ExtKeyUpdateStealthAddress(CHDWalletDB *pwdb, CExtKeyAccount *sea, CKeyID &sxId, std::string &sLabel) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    /**
     * Create an index db record for idKey
     */
    int ExtKeyNewIndex(CHDWalletDB *pwdb, const CKeyID &idKey, uint32_t &index) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyGetIndex(CHDWalletDB *pwdb, CExtKeyAccount *sea, uint32_t &index, bool &fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyGetIndex(CExtKeyAccount *sea, uint32_t &index);

    int NewKeyFromAccount(CHDWalletDB *pwdb, const CKeyID &idAccount, CPubKey &pkOut, bool fInternal, bool fHardened, bool f256bit=false, bool fBech32=false, const char *plabel=nullptr, OutputType output_type=OutputType::LEGACY) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int NewKeyFromAccount(CPubKey &pkOut, bool fInternal=false, bool fHardened=false, bool f256bit=false, bool fBech32=false, const char *plabel=nullptr, OutputType output_type=OutputType::LEGACY); // wrapper - use default account

    int NewStealthKeyFromAccount(CHDWalletDB *pwdb, const CKeyID &idAccount, const std::string &sLabel, CEKAStealthKey &akStealthOut, uint32_t nPrefixBits, const char *pPrefix, bool fBech32=false, uint32_t *pscankey_num=nullptr, bool add_to_lookahead=true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int NewStealthKeyFromAccount(const std::string &sLabel, CEKAStealthKey &akStealthOut, uint32_t nPrefixBits, const char *pPrefix, bool fBech32=false); // wrapper - use default account

    int InitAccountStealthV2Chains(CHDWalletDB *pwdb, CExtKeyAccount *sea) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int SaveStealthAddress(CHDWalletDB *pwdb, CExtKeyAccount *sea, const CEKAStealthKey &akStealth, bool fBech32) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int NewStealthKeyV2FromAccount(CHDWalletDB *pwdb, const CKeyID &idAccount, const std::string &sLabel, CEKAStealthKey &akStealthOut, uint32_t nPrefixBits, const char *pPrefix, bool fBech32=false, uint32_t *pscankey_num=nullptr, uint32_t *pspendkey_num=nullptr, bool add_to_lookahead=true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int NewStealthKeyV2FromAccount(const std::string &sLabel, CEKAStealthKey &akStealthOut, uint32_t nPrefixBits, const char *pPrefix, bool fBech32=false); // wrapper - use default account

    int NewExtKeyFromAccount(CHDWalletDB *pwdb, const CKeyID &idAccount, std::string &sLabel, CStoredExtKey *sekOut, const char *plabel=nullptr, const uint32_t *childNo=nullptr, bool fHardened=false, bool fBech32=false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int NewExtKeyFromAccount(std::string &sLabel, CStoredExtKey *sekOut, const char *plabel=nullptr, const uint32_t *childNo=nullptr, bool fHardened=false, bool fBech32=false); // wrapper - use default account

    int ExtKeyGetDestination(const CExtKeyPair &ek, CPubKey &pkDest, uint32_t &nKey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int ExtKeyUpdateLooseKey(const CExtKeyPair &ek, uint32_t nKey, bool fAddToAddressBook) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    bool GetFullChainPath(const CExtKeyAccount *pa, size_t nChain, std::vector<uint32_t> &vPath) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    /**
     * Insert additional inputs into the transaction by
     * calling CreateTransaction();
     */
    bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
    bool SignTransaction(CMutableTransaction& tx) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    /** Reset transaction creation state */
    void ClearTxCreationState();

    /**
     * Create a new transaction paying the recipients with a set of coins
     * selected by SelectCoins(); Also create the change output, when needed
     * @note passing nChangePosInOut as -1 will result in setting a random position
     */
    bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut,
                           bilingual_str& strFailReason, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
    bool CreateTransaction(std::vector<CTempRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut,
                           bilingual_str& strFailReason, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
    /** Test if mempool would accept tx */
    bool TestMempoolAccept(const CTransactionRef &tx, std::string &sError, CAmount override_max_fee=-1) const;
    /**
     * Submit the transaction to the node's mempool and then relay to peers.
     * Should be called after CreateTransaction unless you want to abort
     * broadcasting the transaction.
     *
     * @param[in] tx The transaction to be broadcast.
     * @param[in] mapValue key-values to be set on the transaction.
     * @param[in] orderForm BIP 70 / BIP 21 order form details to be set on the transaction.
     */
    void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm) override;
    bool CommitTransaction(CWalletTx &wtxNew, CTransactionRecord &rtx, TxValidationState &state,
                           mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm,
                           bool is_record=false, bool broadcast_tx=true, CAmount override_max_fee=-1);

    bool DummySignInput(CTxIn &tx_in, const CTxOutBaseRef &txout) const;
    bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOutBaseRef> &txouts) const;

    int LoadStealthAddresses();
    int LoadMasterKeys();
    bool IndexStealthKey(CHDWalletDB *pwdb, uint160 &hash, const CStealthAddressIndexed &sxi, uint32_t &id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetStealthKeyIndex(CHDWalletDB *pwdb, const CStealthAddressIndexed &sxi, uint32_t &id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetStealthKeyIndex(const CStealthAddressIndexed &sxi, uint32_t &id);
    bool UpdateStealthAddressIndex(const CKeyID &idK, const CStealthAddressIndexed &sxi, uint32_t &id); // Get stealth index or create new index if none found
    bool GetStealthByIndex(uint32_t sxId, CStealthAddress &sx) const;
    bool GetStealthLinked(CHDWalletDB *pwdb, const CKeyID &idK, CStealthAddress &sx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetStealthLinked(const CKeyID &idK, CStealthAddress &sx) const;
    bool GetStealthSecret(const CStealthAddress &sx, CKey &key_out) const;
    bool ProcessLockedStealthOutputs() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool ProcessLockedBlindedOutputs() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool CountRecords(std::string sPrefix, int64_t rv);

    void ProcessStealthLookahead(CExtKeyAccount *ea, const CEKAStealthKey &aks, bool v2) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool ProcessStealthOutput(const CTxDestination &address,
        std::vector<uint8_t> &vchEphemPK, uint32_t prefix, bool fHavePrefix, CKey &sShared, bool fNeedShared=false);

    int CheckForStealthAndNarration(const CTxOutBase *pb, const CTxOutData *pdata, std::string &sNarr);
    void FindStealthTransactions(const CTransaction &tx, mapValue_t &mapNarr);

    bool ScanForOwnedOutputs(const CTransaction &tx, size_t &nCT, size_t &nRingCT, mapValue_t &mapNarr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    int UnloadSpent(const uint256 &wtxid, int depth, const uint256 &wtxid_from);
    void PostProcessUnloadSpent();

    using CWallet::AddToSpends;
    bool HaveSpend(const COutPoint &outpoint, const uint256 &txid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    void AddToSpends(const CWalletTx& wtx, WalletBatch* batch = nullptr) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxState& state, bool fUpdate, bool rescanning_old_block) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    CWalletTx *GetTempWalletTx(const uint256& hash);

    const CWalletTx *GetWalletTx(const uint256& hash) const override;
    CWalletTx *GetWalletTx(const uint256& hash);

    void SetTempTxnStatus(CWalletTx &wtx, const CTransactionRecord *rtx) const;
    int InsertTempTxn(const uint256 &txid, const CTransactionRecord *rtx) const;
    const CWalletTx *GetWalletOrTempTx(const uint256& hash, const CTransactionRecord *rtx) const;

    int OwnStandardOut(const CTxOutStandard *pout, const CTxOutData *pdata,
        COutputRecord &rout, bool &fUpdated, bool tx_is_from_me) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int OwnBlindOut(CHDWalletDB *pwdb, const uint256 &txhash, const CTxOutCT *pout, const CStoredExtKey *pc, uint32_t &nLastChild,
        COutputRecord &rout, CStoredTransaction &stx, bool &fUpdated, bool tx_is_from_me) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    int OwnAnonOut(CHDWalletDB *pwdb, const uint256 &txhash, const CTxOutRingCT *pout, const CStoredExtKey *pc, uint32_t &nLastChild,
        COutputRecord &rout, CStoredTransaction &stx, bool &fUpdated, bool tx_is_from_me) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    bool ProcessPlaceholder(const CTransaction &tx, CTransactionRecord &rtx);
    bool AddToRecord(CTransactionRecord &rtxIn, const CTransaction &tx, const TxState &state, bool fFlushOnClose=true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate, const bool save_progress) override;
    std::vector<uint256> ResendRecordTransactionsBefore(int64_t nTime);
    void ResubmitWalletTransactions(bool relay, bool force) override;

    /**
     * populate vCoins with vector of available COutputs.
     */
    CoinsResult AvailableCoins(const CCoinControl *coinControl = nullptr, std::optional<CFeeRate> feerate = std::nullopt, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    std::optional<SelectionResult> SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue,
        const CCoinControl& coin_control, const CoinSelectionParams& coin_selection_params) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    void AvailableBlindedCoins(std::vector<COutputR>& vCoins, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool SelectBlindedCoins(const std::vector<COutputR>& vAvailableCoins, const CAmount& nTargetValue, std::vector<std::pair<MapRecords_t::const_iterator,unsigned int> > &setCoinsRet, CAmount &nValueRet, const CCoinControl *coinControl = nullptr, bool random_selection = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    void AvailableAnonCoins(std::vector<COutputR> &vCoins, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    const CTxOutBase* FindNonChangeParentOutput(const CTransaction& tx, int output) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetAddressFromOutputRecord(const uint256 &txhash, const COutputRecord *pout, CTxDestination &address) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetFirstNonChangeAddress(const uint256 &hash, const CTransactionRecord &txr, const COutputRecord *pout, CTxDestination &address) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    /**
     * Return list of available coins and locked coins grouped by non-change output address.
     */
    std::map<CTxDestination, std::vector<COutput>> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    std::map<CTxDestination, std::vector<COutputR>> ListCoins(OutputTypes nType) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    bool AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutputR> vCoins, std::vector<std::pair<MapRecords_t::const_iterator,unsigned int> > &setCoinsRet, CAmount &nValueRet) const;

    bool IsSpent(const COutPoint& outpoint) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    bool GetSpendingTxid(const uint256& hash, unsigned int n, uint256 &spent_by_txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    /** Whether this or any UTXO with the same CTxDestination has been spent. */
    bool IsSpentKey(const CScript& scriptPubKey) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    void SetSpentKeyState(const CScript *pscript, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    void SetSpentKeyState(const uint256& hash, unsigned int n, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
    void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    std::set<uint256> GetConflicts(const uint256 &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    /** Return whether transaction can be abandoned */
    bool TransactionCanBeAbandoned(const uint256& hashTx) const override;

    /** Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
    bool AbandonTransaction(const uint256 &hashTx) override;

    /** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
    void MarkConflicted(const uint256 &hashBlock, int conflicting_height, const uint256 &hashTx) override;
    void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    //! Check if a given transaction record has any of its outputs spent by another transaction in the wallet
    bool HasWalletSpend(const uint256 hash, const CTransactionRecord &rtx) const;

    bool GetSetting(const std::string &setting, UniValue &json) const;
    bool SetSetting(const std::string &setting, const UniValue &json);
    bool EraseSetting(const std::string &setting);

    int64_t GetTimeFirstKey();

    /** Return a prevout if it exists in the wallet. */
    bool GetPrevout(const COutPoint &prevout, CTxOutBaseRef &txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

    size_t CountColdstakeOutputs();
    void ClearMapTempRecords();

    /** Return a script for any destination type (normal/stealth/extended) */
    bool GetScriptForDest(CScript &script, const CTxDestination &dest, bool fUpdate = false, std::vector<uint8_t> *vData = nullptr, uint32_t *last_key = nullptr);

    bool SetReserveBalance(CAmount nNewReserveBalance);
    void SetStakeLimitHeight(int stake_limit);
    uint64_t GetStakeWeight() const;
    void AvailableCoinsForStaking(std::vector<COutput> &vCoins, int64_t nTime, int nHeight) const;
    bool SelectCoinsForStaking(int64_t nTargetValue, int64_t nTime, int nHeight, std::set<COutput> &setCoinsRet, int64_t &nValueRet) const;
    bool CreateCoinStake(unsigned int nBits, int64_t nTime, int nBlockHeight, int64_t nFees, CMutableTransaction &txNew, CKey &key);
    bool SignBlock(node::CBlockTemplate *pblocktemplate, int nHeight, int64_t nSearchTime);
    std::unique_ptr<node::CBlockTemplate> CreateNewBlock();

    boost::signals2::signal<void (CAmount nReservedBalance)> NotifyReservedBalanceChanged;

    size_t CountTxSpends() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { return mapTxSpends.size(); };

    int64_t nLastCoinStakeSearchTime = 0;
    uint32_t nStealth, nFoundStealth; // for reporting, zero before use
    int64_t nReserveBalance = 0;
    size_t nStakeThread = 9999999; // unset

    mutable int m_greatest_txn_depth = 0; // depth of most deep txn
    //mutable int m_least_txn_depth = 0; // depth of least deep txn
    mutable std::atomic_bool m_have_spendable_balance_cached {false};
    mutable CAmount m_spendable_balance_cached = 0;

    enum eStakingState {
        NOT_STAKING = 0,
        IS_STAKING = 1,
        NOT_STAKING_BALANCE = -1,
        NOT_STAKING_DEPTH = -2,
        NOT_STAKING_LOCKED = -3,
        NOT_STAKING_LIMITED = -4,
        NOT_STAKING_DISABLED = -5,
    };
    std::atomic<eStakingState> m_is_staking {NOT_STAKING};

    std::set<CStealthAddress> stealthAddresses;

    CStoredExtKey *pEKMaster = nullptr;
    CKeyID idDefaultAccount;
    ExtKeyAccountMap mapExtAccounts;
    ExtKeyMap mapExtKeys;
    mutable LooseKeyMap mapLooseKeys;       // Keys derived from extkeys not attached to an account
    mutable LooseKeyMap mapLooseLookAhead;

    mutable MapWallet_t mapTempWallet;  // TODO: Remove

    MapRecords_t mapRecords;
    RtxOrdered_t rtxOrdered;
    mutable MapRecords_t mapTempRecords; // Hack for sending unmined inputs through fundrawtransactionfrom

    std::vector<CVoteToken> vVoteTokens;

    // Staking Settings
    std::atomic<bool> fStakingEnabled{false};
    CAmount nStakeCombineThreshold;
    CAmount nStakeSplitThreshold;
    size_t nMaxStakeCombine = 3;
    int nWalletTreasuryFundCedePercent;
    CTxDestination m_reward_address = CNoDestination();
    int nStakeLimitHeight = 0; // for regtest, don't stake above nStakeLimitHeight

    mutable std::atomic_bool m_have_cached_stakeable_coins {false};
    mutable std::vector<COutput> m_cached_stakeable_coins;

    bool fUnlockForStakingOnly = false; // Use coldstaking instead

    int64_t nRCTOutSelectionGroup1 = 5000;
    int64_t nRCTOutSelectionGroup2 = 50000;
    size_t prefer_max_num_anon_inputs = 5; // if > x anon inputs are randomly selected attempt to reduce
    int m_mixin_selection_mode_default = 1;
    secp256k1_scratch_space *m_blind_scratch = nullptr;

    int m_collapse_spent_mode = 0;
    int m_min_collapse_depth = 3;
    std::map<uint256, std::set<uint256> > mapTxCollapsedSpends;
    std::set<uint256> m_collapsed_txns;
    std::set<COutPoint> m_collapsed_txn_inputs;

    int64_t m_smsg_fee_rate_target = 0;
    uint32_t m_smsg_difficulty_target = 0; // 0 = auto
    bool m_is_only_instance = true; // Set to false if spends can happen in a different wallet

    size_t m_rescan_stealth_v1_lookahead = DEFAULT_STEALTH_LOOKAHEAD_SIZE;
    size_t m_rescan_stealth_v2_lookahead = DEFAULT_STEALTH_LOOKAHEAD_SIZE;
    size_t m_default_lookahead = DEFAULT_LOOKAHEAD_SIZE;

    bool m_smsg_enabled = true;
    CAmount m_min_stakeable_value = 1;  // Wallet will not try to stake outputs below this value
    CAmount m_min_owned_value = 0;      // Wallet will ignore outputs below this value

    std::map<CKeyID, uint32_t> m_derived_keys; // Allows multiple provisional derivations from the same extkey

private:
    void ParseAddressForMetaData(const CTxDestination &addr, COutputRecord &rec);

    template<typename... Params>
    bool werror(std::string fmt, Params... parameters) const {
        return error(("%s " + fmt).c_str(), GetDisplayName(), parameters...);
    }
    template<typename... Params>
    int werrorN(int rv, std::string fmt, Params... parameters) const {
        return errorN(rv, ("%s " + fmt).c_str(), GetDisplayName(), parameters...);
    }
    template<typename... Params>
    int wserrorN(int rv, std::string &s, const char *func, std::string fmt, Params... parameters) const {
        return errorN(rv, s, func, ("%s " + fmt).c_str(), GetDisplayName(), parameters...);
    }
};

class LoopExtKeyCallback
{
public:
    CHDWallet *pwallet = nullptr;

    // NOTE: the key and account instances passed to Process are temporary
    virtual int ProcessKey(CKeyID &id, CStoredExtKey &sek) {return 1;};
    virtual int ProcessAccount(CKeyID &id, CExtKeyAccount &sek) {return 1;};
};

int LoopExtKeysInDB(CHDWallet *pwallet, bool fInactive, bool fInAccount, LoopExtKeyCallback &callback) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet);
int LoopExtAccountsInDB(CHDWallet *pwallet, bool fInactive, LoopExtKeyCallback &callback) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet);

bool CheckOutputValue(interfaces::Chain& chain, const CTempRecipient &r, const CTxOutBase *txbout, CAmount nFeeRet, std::string &sError);
int CreateOutput(OUTPUT_PTR<CTxOutBase> &txbout, CTempRecipient &r, std::string &sError);
void ExtractNarration(const uint256 &nonce, const std::vector<uint8_t> &vData, std::string &sNarr);

// Calculate the size of the transaction assuming all signatures are max size
// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
// be IsAllFromMe).
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CHDWallet *wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CHDWallet *wallet, const std::vector<CTxOutBaseRef>& txouts);

void RestartStakingThreads(WalletContext &wallet_context, ChainstateManager &chainman);

bool IsParticlWallet(const WalletStorage *win);
CHDWallet *GetParticlWallet(WalletStorage *win);
const CHDWallet *GetParticlWallet(const WalletStorage *win);

#endif // PARTICL_WALLET_HDWALLET_H

redpajama_set_name: RedPajamaGithub


text:
Introduction
============
Have you developed a client side, JavaScript generated, web site or
application and want it indexed?
There are several tools out there for you but I could not find one that
met my needs.
This is why I created `PhantomSnap`.

What it is
----------
`PhantomSnap` is a tool to automatically generate HTML snapshots of your site
or application.
You give it the location of your code, a destination directory and a main URL.

`PhantomSnap` will fire up an HTTP server pointing at your sources and will use
[PhantomJS](http://phantomjs.org/) to load your main page and crawl its way
through your website, storing HTML versions of you pages into the
destination directory.

What it is *NOT*
----------------
  * A server to compile your URLs on the fly (it turns out that with my design
    it is possible to do this too but I did not get around to do it).
  * A way to test/check/verify your site or application.
  * An optimised version of [PhantomJS](http://phantomjs.org/).

Current limitations
-------------------
  * Timeouts on operations are an easy way to recover from unexpected errors
    of your site or application but they will be implemented in later
    iterations.
  * Error reporting and retries are a bit more complex that timeouts but
    are planned for feature releases as well.
  * Project not yet available on `NPM`.
  * Only usable through the `Grunt` task or by implementing the main
    function to instantiate all components manually.

Known issues
------------
  * Crawling sites or applications on top of previous snapshots may cause
    errors where files try to overwrite directories.
  * It usually happened around failing tests that did not clean up properly
    but it might happen that the [PhantomJS](http://phantomjs.org/) instance
    does not terminate.
    This could lead to unexpected behaviours as only one for each crawler
    is assumed.

Usage
=====
Using `PhantomSnap` is all about passing the right options to the grunt task.
Since there are quite a few options, all documented in the docstrings on the
constructors of the relevant classes (the section "Organisation of the code"
may help you figure this out), I will be focusing on the primarily target
usage: crawling a website or application as part of the Grunt pipeline.

Installing
----------
First of all you will need to add `PhantomSnap` to your `package.json`.
As I said it is not yet on `NPM` but that should not stop you.
Simply add this:
```javascript
"phantom-snap": "git+ssh://git@github.com:stefano-pogliani/phantom-snap.git#release/latest"
```

Note that `release/latest` should be changed to a fixed version to avoid
updates breaking your project unexpectedly.
`NPM` does not support versions when fetching directly from git.

Once `PhantomSnap` is `npm install`ed it can be loaded in `Grunt` with
```javascript
grunt.loadNpmTasks("phantom-snap");
```

Options are specified in the `snapshot` task and can be both in the object
as well as in the `options` property of that object.
The following example will be used to explain some of the most common options:
```javascript
snapshot: {
  verbose_logging: true,
  crawler: {
    base_path: path.join(__dirname, "seo", "snapshot"),
    index:     "/"
  },
  fetcher: {
    base_url:       "http://localhost:8080/index.html#!",
    waiter_options: {
      path:     "./waiters/css-polling",
      property: "display",
      selector: "#route-loading",
      value:    "none"
    }
  },
  static: { path: path.join(__dirname, "out") }
}
```

  * `verbose_logging` will make PhantomSnap output logs at debug level.
    When off (the default) no logs are printed (none at all, not yet at least).
    While usually you should not need them, you may be curious or just want to
    double check that the process is actually doing something.
  * The `crawler` is the component that drives the whole process.
    * `base_path` is the destination directory for the snapshot.
    * `index` is the URI of the page to load first.
  * The `fetcher` is the component that loads individual pages in
    [PhantomJS](http://phantomjs.org/).
    * `base_url` is prefixed to each page URI to build the full URL to fetch.
      This example is taken from a "single page" web site that uses the
      hash-bang (`#!`) syntax for routes.
    * `waiter_options` is the reason regular crawling tools don't cut it.
      It is not difficult to understand but it is very important so I will
      explain it on its own.
  * `static` is a static server that will serve files out of `path`.
    If you prefer other ways to serve your application or web site or it
    requires more than a static server omit this option.
    If this option is omitted no server is started and you can use the
    `base_url` in the `fetcher` to access the pages.

### Load Waiters
The main problem with crawling JavaScript generated content is that the
crawler has to wait for the content to be actually ready and it cannot
rely on availability of the HTML or the document loaded event.

Thanks to project like [PhantomJS](http://phantomjs.org/) running the pages
themselves is now easy but waiting for the content is still tricky since each
site and application have different ways to represent the waiting for content.

_Load waiters_ are the tools used by the fetcher to know when the page is
finally ready.
By default `PhantomSnap` comes with the following waiters:

  * The `noop` waiter immediately returns and does not have any option.
  * The `css-polling` waiter looks for an element in the document with a
    specific CSS property.
    The options are used to specify the condition to wait for:
    The `property` is a CSS property name, in browser syntax, to look up on
    the first element found using the `selector` and to check for equality
    with `value`.

Additional waiters will be implemented in future iterations or can be
implemented to fit your needs.

Extending and/or contributing
=============================
This project is a [NodeJS](http://nodejs.org/) application that controls a
[PhantomJS](http://phantomjs.org/) instance.
This is done by serving a control page from [NodeJS](http://nodejs.org/)
which will use web sockets to communicate with PhantomJS.

When the PhantomJS instance is started the control page served by NodeJS is
opened and used to receive commands and send responses.

One project, three JavaScripts
------------------------------
As a result of this there are three variations of JavaScript around the project.
The outer shell is written in Node and is located in the `src/` directory
of the project.

The second layer is in PhantomJS, which is similar to Node when it comes to
the modules system and the `fs` module but different otherwise.
The PhantomJS sources are located under `src/phantomjs` with the exception of
`src/phantomjs/wrapper.js` which is the NodeJS side of the controller.
PhantomJS execution starts from `src/phantomjs/driver.js`, which does very
little.

The final layer in this stack is the JavaScript that runs in PhantomJS pages
themselves.
This includes the code in `src/phantomjs/front`, which is the client side
web socket channel, and the code injected into the pages through the `evaluate`
function.
Although the `evaluate` function is used in several places the main
concentration is in the waiters, found in `src/phantomjs/waiters`.

Writing waiters
---------------
Waiters are PhantomJS modules that export a factory function that receives the
options defined in the `waiter_options` portion of the configuration.
The value returned by the factory function is assumed to have a `wait`
function that takes a PhantomJS page instance and a ready callback.
The the waiter determines that the page is ready it should invoke the
callback without arguments.
For examples and documentation you can refer to the existing `noop` and
`css-polling` waiters in `src/phantomjs/waiters`.

License
=======
The MIT License (MIT)

Copyright (c) 2014 Stefano Pogliani

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


text:
define( [
    'osg/Utils',
    'osg/Notify',
    'osg/Object',
    'osgAnimation/FindNearestParentSkeleton'
], function ( MACROUTILS, Notify, ObjectBase, FindNearestParentSkeleton ) {

    'use strict';

    // converted from C++ probably it could be merged into RigGeometry
    // it could probably inlined into RigGeometry code
    var UpdateRigGeometry = function () {
        ObjectBase.call( this );
    };

    UpdateRigGeometry.prototype = MACROUTILS.objectInherit( ObjectBase.prototype, {

        init: function ( geom ) {

            var finder = new FindNearestParentSkeleton();
            if ( geom.getParents().length > 1 )
                Notify.warn( 'A RigGeometry should not have multi parent ( ' + geom.getName() + ' )' );

            geom.getParents()[ 0 ].accept( finder );

            if ( !finder._root ) {
                Notify.warn( 'A RigGeometry did not find a parent skeleton for RigGeometry ( ' + geom.getName() + ' )' );
                return;
            }

            geom.setSkeleton( finder._root );
        },

        update: function ( node /*, nv*/ ) {

            // Circular ref
            if ( node && node.className() !== 'RigGeometry' ) return true;

            var geom = node;

            // maybe this code could simpler
            if ( !geom.getSkeleton() && geom.getParents().length !== 0 ) this.init( geom );
            if ( !geom.getSkeleton() ) return true;

            if ( geom.getNeedToComputeMatrix() ) geom.computeMatrixFromRootSkeleton();

            geom.update();

            return true;
        }

    } );

    /* virtual void update(osg::NodeVisitor*, osg::Drawable* drw) {
     RigGeometry* geom = dynamic_cast<RigGeometry*>(drw);
     if(!geom)
     return;
     if(!geom->getSkeleton() && !geom->getParents().empty())
     {
     RigGeometry::FindNearestParentSkeleton finder;
     if(geom->getParents().size() > 1)
     osg::notify(osg::WARN) << "A RigGeometry should not have multi parent ( " << geom->getName() << " )" << std::endl;
     geom->getParents()[0]->accept(finder);

     if(!finder._root.valid())
     {
     osg::notify(osg::WARN) << "A RigGeometry did not find a parent skeleton for RigGeometry ( " << geom->getName() << " )" << std::endl;
     return;
     }
     geom->buildVertexInfluenceSet();
     geom->setSkeleton(finder._root.get());
     }

     if(!geom->getSkeleton())
     return;

     if(geom->getNeedToComputeMatrix())
     geom->computeMatrixFromRootSkeleton();

     geom->update();
     }*/

    return UpdateRigGeometry;

} );


text: java -jar RunMain.jar


text:
Patterns
========

Patterns


text:
/* A Bison parser, made by GNU Bison 2.3.  */

/* Skeleton interface for Bison's Yacc-like parsers in C

   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
   Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.  */

/* As a special exception, you may create a larger work that contains
   part or all of the Bison parser skeleton and distribute that work
   under terms of your choice, so long as that work isn't itself a
   parser generator using the skeleton or a modified version thereof
   as a parser skeleton.  Alternatively, if you modify or redistribute
   the parser skeleton itself, you may (at your option) remove this
   special exception, which will cause the skeleton and the resulting
   Bison output files to be licensed under the GNU General Public
   License without this special exception.

   This special exception was added by the Free Software Foundation in
   version 2.2 of Bison.  */

/* Tokens.  */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
   /* Put the tokens into the symbol table, so that GDB and other debuggers
      know about them.  */
   enum yytokentype {
     INT_LITERAL = 258,
     DOUBLE_LITERAL = 259,
     STRING_LITERAL = 260,
     IDENTIFIER = 261,
     FUNCTION = 262,
     IF = 263,
     ELSE = 264,
     ELSIF = 265,
     WHILE = 266,
     FOR = 267,
     REPEAT = 268,
     LOOP = 269,
     RETURN_T = 270,
     BREAK = 271,
     CONTINUE = 272,
     NULL_T = 273,
     LP = 274,
     RP = 275,
     LC = 276,
     RC = 277,
     LB = 278,
     RB = 279,
     SEMICOLON = 280,
     COLON = 281,
     DOUBLE_COLON = 282,
     AT_LC = 283,
     AT_RC = 284,
     COMMA = 285,
     ASSIGN_T = 286,
     LOGICAL_AND = 287,
     LOGICAL_OR = 288,
     EQ = 289,
     NE = 290,
     GT = 291,
     GE = 292,
     LT = 293,
     LE = 294,
     ADD = 295,
     SUB = 296,
     MUL = 297,
     DIV = 298,
     MOD = 299,
     TRUE_T = 300,
     FALSE_T = 301,
     EXCLAMATION = 302,
     DOT = 303,
     ADD_ASSIGN_T = 304,
     SUB_ASSIGN_T = 305,
     MUL_ASSIGN_T = 306,
     DIV_ASSIGN_T = 307,
     MOD_ASSIGN_T = 308,
     INCREMENT = 309,
     DECREMENT = 310,
     CLOSURE = 311,
     GLOBAL_T = 312,
     TRY = 313,
     CATCH = 314,
     FINALLY = 315
   };
#endif
/* Tokens.  */
#define INT_LITERAL 258
#define DOUBLE_LITERAL 259
#define STRING_LITERAL 260
#define IDENTIFIER 261
#define FUNCTION 262
#define IF 263
#define ELSE 264
#define ELSIF 265
#define WHILE 266
#define FOR 267
#define REPEAT 268
#define LOOP 269
#define RETURN_T 270
#define BREAK 271
#define CONTINUE 272
#define NULL_T 273
#define LP 274
#define RP 275
#define LC 276
#define RC 277
#define LB 278
#define RB 279
#define SEMICOLON 280
#define COLON 281
#define DOUBLE_COLON 282
#define AT_LC 283
#define AT_RC 284
#define COMMA 285
#define ASSIGN_T 286
#define LOGICAL_AND 287
#define LOGICAL_OR 288
#define EQ 289
#define NE 290
#define GT 291
#define GE 292
#define LT 293
#define LE 294
#define ADD 295
#define SUB 296
#define MUL 297
#define DIV 298
#define MOD 299
#define TRUE_T 300
#define FALSE_T 301
#define EXCLAMATION 302
#define DOT 303
#define ADD_ASSIGN_T 304
#define SUB_ASSIGN_T 305
#define MUL_ASSIGN_T 306
#define DIV_ASSIGN_T 307
#define MOD_ASSIGN_T 308
#define INCREMENT 309
#define DECREMENT 310
#define CLOSURE 311
#define GLOBAL_T 312
#define TRY 313
#define CATCH 314
#define FINALLY 315

#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
#line 6 "crowbar.y"
{
    char                *identifier;
    ParameterList       *parameter_list;
    ArgumentList        *argument_list;
    Expression          *expression;
    ExpressionList      *expression_list;
    Statement           *statement;
    StatementList       *statement_list;
    Block               *block;
    Elsif               *elsif;
    AssignmentOperator  assignment_operator;
    IdentifierList      *identifier_list;
}
/* Line 1529 of yacc.c.  */
#line 183 "y.tab.h"
	YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
# define YYSTYPE_IS_TRIVIAL 1
#endif

extern YYSTYPE yylval;