find and wrap logical operators AND, OR, (, )
@param $parts
@return array
347481
protected function _mongo_parse_logical_op($parts) {
		$b_offset = 0;
		$ncond = array();
		$child = array();
		for ($i = 0, $max = count($parts); $i < $max; $i++) {
			$part = $parts[$i];
			if (is_string($part))
				$part = trim($part);
			if ($part == '(') {
				// add sub-bracket to parse array
				if ($b_offset > 0)
					$child[] = $part;
				$b_offset++;
			} elseif ($part == ')') {
				$b_offset--;
				// found closing bracket
				if ($b_offset == 0) {
					$ncond[] = ($this->_mongo_parse_logical_op($child));
					$child = array();
				} elseif ($b_offset < 0)
					trigger_error(self::E_BRACKETS,E_USER_ERROR);
				else
					// add sub-bracket to parse array
					$child[] = $part;
			}
			elseif ($b_offset > 0) {
				// add to parse array
				$child[]=$part;
				// condition type
			} elseif (!is_array($part)) {
				if (strtoupper($part) == 'AND')
					$add = true;
				elseif (strtoupper($part) == 'OR')
					$or = true;
			} else // skip
				$ncond[] = $part;
		}
		if ($b_offset > 0)
			trigger_error(self::E_BRACKETS,E_USER_ERROR);
		if (isset($add))
			return array('$and' => $ncond);
		elseif (isset($or))
			return array('$or' => $ncond);
		else
			return $ncond[0];
	}
134935
public function map(array $parts): array
    {
        if ($this->isMatchingSinglePart($parts)) {
            $parts[0] = new Suffix($parts[0], $this->suffixes[$this->getKey($parts[0])]);
            return $parts;
        }

        $start = count($parts) - 1;

        for ($k = $start; $k > 1; $k--) {
            $part = $parts[$k];

            if (!$this->isSuffix($part)) {
                break;
            }

            $parts[$k] = new Suffix($part, $this->suffixes[$this->getKey($part)]);
        }

        return $parts;
    }
1158887
protected function _splitCheck($parts, $char)
    {
        $string = $parts[0];

        for ($i = 0; $i < count($parts); $i++) {
            if ($this->_hasUnclosedQuotes($string)
                || $this->_hasUnclosedBrackets($string, '<>')
                || $this->_hasUnclosedBrackets($string, '[]')
                || $this->_hasUnclosedBrackets($string, '()')
                || substr($string, -1) == '\\') {
                if (isset($parts[$i + 1])) {
                    $string = $string . $char . $parts[$i + 1];
                } else {
                    $this->error = 'Invalid address spec. Unclosed bracket or quotes';
                    return false;
                }
            } else {
                $this->index = $i;
                break;
            }
        }

        return $string;
    }
354964
protected function mapParts(array $parts): array
    {
        $k = $this->skipIgnoredParts($parts) + 1;
        $remapIgnored = true;

        while (--$k >= 0) {
            $part = $parts[$k];

            if ($part instanceof AbstractPart) {
                break;
            }

            if ($this->isFollowedByLastnamePart($parts, $k)) {
                if ($this->isApplicablePrefix($parts, $k)) {
                    $parts[$k] = new LastnamePrefix($part, $this->prefixes[$this->getKey($part)]);
                    continue;
                }

                if ($this->shouldStopMapping($parts, $k)) {
                    break;
                }
            }

            $parts[$k] = new Lastname($part);
            $remapIgnored = false;
        }

        if ($remapIgnored) {
            $parts = $this->remapIgnored($parts);
        }

        return $parts;
    }
190919
public function visitLogicalExpression(Expr\LogicalExpression $expr)
    {
        $exprs = array();
        foreach($expr->getExpressions() as $innerExpr) {
            $exprs[] = $this->visit($innerExpr);
        }

        switch($expr->getType()) {
        case Expr\LogicalExpression::TYPE_AND:
            if(1 >= count($exprs)) {
                return implode(' ', $exprs);
            }
            return Literals::L_AND . Literals::L_COLON . 
                Literals::L_PARENTHESIS_OPEN . 
                implode(' ', $exprs) .
                Literals::L_PARENTHESIS_CLOSE ;
        case Expr\LogicalExpression::TYPE_OR:
            if(1 >= count($exprs)) {
                return implode(' ', $exprs);
            }
            return Literals::L_OR . Literals::L_COLON . 
                Literals::L_PARENTHESIS_OPEN . 
                implode(' ', $exprs) .
                Literals::L_PARENTHESIS_CLOSE ;
        case Expr\LogicalExpression::TYPE_NOT:
            return Literals::L_NOT . Literals::L_COLON . 
                Literals::L_PARENTHESIS_OPEN . 
                implode(' ', $exprs) .
                Literals::L_PARENTHESIS_CLOSE ;
        default:
            throw new \RuntimeException(sprintf('Unknown type of LogicalExpression operator: [%s]', (string)$expr->getType()));
        }
    }
34591
protected static function replaceOptionalSegments($regex)
    {
        preg_match(chr(1) . '\(/([a-z][a-zA-Z0-9_,]*)\)' . chr(1), $regex, $matches);

        if (!$matches) {
            return $regex;
        }

        $list = explode(',', $matches[1]);
        $head = '';

        if (substr($regex, 0, 2) === '(/') {
            $name = array_shift($list);
            $head = "/(\{{$name}\})?";
        }

        $tail = '';

        foreach ($list as $name) {
            $head .= "(/({$name})";
            $tail .= ')?';
        }

        $repl = $head . $tail;

        return str_replace($matches[0], $repl, $regex);
    }
1009254
public function getBinds($parts = [], $clear = false)
    {
        $binds = [];

        if (!is_array($parts)) {
            $parts = [$parts];
        }

        if (!$parts) {
            $parts = array_keys($this->bind);
        }

        foreach ($parts as $part) {
            if (!isset($this->bind[$part])) {
                continue;
            }

            foreach ($this->bind[$part] as $key => $bind) {
                $binds[] = $bind;
                if ($clear) {
                    unset($this->bind[$part][$key]);
                }
            }
        }

        return $binds;
    }
1244379
protected function parseDocumentTitle(array $parts)
    {
        $parts = $this->parseDocumentTitleParts($parts);
        $delim = $this->parseDocumentTitleSeparator();
        $title = implode($delim, $parts);

        return $title;
    }
1368195
public function extract($parts = null) {
        if ($parts === null) {
            return $this->data;
        }

        if (is_array($parts)) {
            $result = array();

            foreach ($this->data as $match) {
                $row = array();
                foreach ($parts as $key) {
                    if (isset($match[$key])) {
                        $row[$key] = isset($match[$key])?$match[$key]:null;
                    }
                }

                $result[] = $row;
            }

            return $result;
        }

        $result = array();
        foreach ($this->data as $match) {
            $result[] = isset($match[$parts])?$match[$parts]:null;
        }

        return $result;
    }
1220493
function _findOperator($name)
    {
        if (empty($name)) {
            return '==';
        } elseif (isset($this->_operators[$name])) {
            return $this->_operators[$name];
        } elseif (in_array($name, $this->_operators)) {
            return $name;
        } else {
            return '==';
        }
    }
1330051
private function parseCode(array $lines, array $parts, $start)
    {
        list ($blank, $lang) = $parts;
        $lang = trim($lang);
        $count = strlen($blank);
        if (!preg_match("/^[_a-z0-9-\+\#\:\.]+$/i", $lang)) {
            $lang = NULL;
        } else {
            $parts = explode(':', $lang);
            if (count($parts) > 1) {
                list ($lang, $rel) = $parts;
                $lang = trim($lang);
                $rel = trim($rel);
            }
        }
        $isEmpty = true;
        $lines = array_map(function ($line) use ($count, &$isEmpty) {
            $line = preg_replace("/^[ ]{{$count}}/", '', $line);
            if ($isEmpty && !preg_match("/^\s*$/", $line)) {
                $isEmpty = false;
            }
            return htmlspecialchars($line);
        }, array_slice($lines, 1, -1));
        
        $str = implode("\n", $this->markLines($lines, $start + 1));
        return $isEmpty ? '' :
            '<pre><code' . (!empty($lang) ? " class=\"{$lang}\"" : '')
            . (!empty($rel) ? " rel=\"{$rel}\"" : '') . '>'
            . $str . '</code></pre>';
    }
733252
public function map(array $parts): array
    {
        if (count($parts) < 2) {
            return [$this->handleSinglePart($parts[0])];
        }

        $pos = $this->findFirstnamePosition($parts);

        if (null !== $pos) {
            $parts[$pos] = new Firstname($parts[$pos]);
        }

        return $parts;
    }
1097550
private function _operatorClassifier($init_conditions)
    {
        $or_rules = array();
        $and_rules = array();
        $first_rule = array();
        $strlen = strlen($init_conditions);
        $counter = $strlen - 1;

        for($i = $strlen - 1; $i >= 0; $i--)
        {
            $char = substr($init_conditions, $i, 1);
            if ($char == "|" || $char == ";"|| $i == 0)
            {
                $rule = substr($init_conditions, $i + 1, $counter - $i);
                $rule = trim($rule);
                if ($char == "|")
                {
                    array_unshift($or_rules, $rule);
                }
                else if ($char == ";")
                {
                    array_unshift($and_rules, $rule);
                }
                else if ($i == 0)
                {
                    $rule = substr($init_conditions, 0,  $counter + 1);
                    $first_rule[] = $rule;
                }
                $counter = $i - 1;
            }
        }
        $operators = array(
            "or_rules" => $or_rules,
            "and_rules" => $and_rules,
            "first_rule" => $first_rule
        );
        return $operators;
    }
85492
private function parseOperators($params)
    {
        foreach ($params as $key => $input) {
            if (is_array($input)) {

            } elseif (substr($input, 0, 1) == "!") {
                $params[$key] = array("NOT" => $this->parseValue(substr($input, 1)));
            } elseif (substr($input, 0, 2) == "><") {
                $params[$key] = array("BETWEEN" => $this->parseValue(substr($input, 2)));
            } elseif (substr($input, 0, 2) == ">=") {
                $params[$key] = array("GTE" => $this->parseValue(substr($input, 2)));
            } elseif (substr($input, 0, 2) == "<=") {
                $params[$key] = array("LTE" => $this->parseValue(substr($input, 2)));
            } elseif (substr($input, 0, 1) == "<") {
                $params[$key] = array("LT" => $this->parseValue(substr($input, 1)));
            } elseif (substr($input, 0, 1) == ">") {
                $params[$key] = array("GT" => $this->parseValue(substr($input, 1)));
            } else {
                $params[$key] = $this->parseValue($input);
            }
        }

        return $params;
    }
470935
protected function _parseConstraints()
    {
        $parts = [];
        foreach ($this->_sql->getConstraints() as $constraint) {
            $cons = $this->_parseConstraint($constraint);
            if ($cons) {
                $parts[] = $cons;
            }
        }
        return implode(', ', $parts);
    }
1152675
public function buildConditions($conditions)
    {
        if (!is_array($conditions)) {
            if (null === $conditions) {
                return '';
            }
            return $conditions;
        } elseif ($conditions === array()) {
            return '';
        }

        $n = count($conditions);
        $operator = strtoupper($conditions[0]);
        if ($operator === 'AND' || $operator === 'OR') {
            $result = array();
            for ($i = 1; $i < $n; $i++) {
                $condition = $this->buildConditions($conditions[$i]);
                if ('' !== $condition) {
                    $result[] = '(' . $condition . ')';
                }
            }
            if ($result === array()) {
                return '';
            } else {
                return implode(' ' . $operator . ' ', $result);
            }
        }

        if (!isset($conditions[1], $conditions[2])) {
            return '';
        }

        $column = $this->db->quoteColumn($conditions[1]);
        $values = $conditions[2];
        if (!is_array($values)) {
            $values = array($values);
        }
        if ($operator === 'IN' || $operator === 'NOT IN') {
            if ($values === array()) {
                return $operator === 'IN' ? '0' : '';
            }
            foreach ($values as $k => $value) {
                $values[$k] = $this->db->quote($value);
            }

            return $column . ' ' . $operator . ' (' . implode(',', $values) . ')';
        } elseif ($operator === 'LIKE' || $operator === 'NOT LIKE' || $operator === 'OR LIKE' || $operator === 'OR NOT LIKE') {
            if ($values === array()) {
                return ($operator === 'LIKE' || $operator === 'OR LIKE') ? '0' : '';
            }
            if ($operator === 'LIKE' || $operator === 'NOT LIKE') {
                $andor = 'AND';
            } else {
                $andor = 'OR';
                $operator = $operator === 'OR LIKE' ? 'LIKE' : 'NOT LIKE';
            }

            $result = array();
            foreach ($values as $k => $value) {
                $result[] = $column . ' ' . $operator . ' ' . $this->db->quote($value);
            }

            return implode($andor, $result);
        } else {
            throw new Exception('Invalid operator "' . $operator . '"');
        }
    }
391238
public function map(array $parts): array
    {
        // If we don't expect a lastname, match a mimimum of 2 parts
        $minumumParts = ($this->mapWithoutLastname ? 2 : 3);

        if (count($parts) < $minumumParts) {
            return $parts;
        }

        $start = $this->findFirstMapped(Firstname::class, $parts);

        if (false === $start) {
            return $parts;
        }

        return $this->mapFrom($start, $parts);
    }