| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_ucb.hxx" |
| |
| #include <limits> |
| #include <com/sun/star/ucb/RuleOperator.hpp> |
| #include <com/sun/star/ucb/SearchInfo.hpp> |
| #include <com/sun/star/util/Date.hpp> |
| #include <tools/date.hxx> |
| #include <tools/inetmime.hxx> |
| #include <tools/string.hxx> |
| |
| #ifndef CHAOS_UCBDEMO_SRCHARG_HXX |
| #include <srcharg.hxx> |
| #endif |
| |
| namespace unnamed_chaos_ucbdemo_srcharg {} |
| using namespace unnamed_chaos_ucbdemo_srcharg; |
| // unnamed namespaces don't work well yet... |
| |
| using namespace com::sun::star; |
| |
| //============================================================================ |
| // |
| // skipWhiteSpace |
| // |
| //============================================================================ |
| |
| namespace unnamed_chaos_ucbdemo_srcharg { |
| |
| void skipWhiteSpace(sal_Unicode const *& rBegin, sal_Unicode const * pEnd) |
| { |
| while (rBegin != pEnd |
| && (*rBegin == '\n' || *rBegin == '\t' || *rBegin == ' ')) |
| ++rBegin; |
| } |
| |
| //============================================================================ |
| // |
| // scanAtom |
| // |
| //============================================================================ |
| |
| String scanAtom(sal_Unicode const *& rBegin, sal_Unicode const * pEnd) |
| { |
| sal_Unicode const * pTheBegin = rBegin; |
| while (rBegin != pEnd && INetMIME::isAlpha(*rBegin)) |
| ++rBegin; |
| return String(pTheBegin, rBegin - pTheBegin); |
| } |
| |
| //============================================================================ |
| // |
| // scanProperty |
| // |
| //============================================================================ |
| |
| String scanProperty(sal_Unicode const *& rBegin, sal_Unicode const * pEnd) |
| { |
| sal_Unicode const * pTheBegin = rBegin; |
| while (rBegin != pEnd |
| && !(*rBegin == '\n' || *rBegin == '\t' || *rBegin == ' ')) |
| ++rBegin; |
| return String(pTheBegin, rBegin - pTheBegin); |
| } |
| |
| //============================================================================ |
| // |
| // scanOperator |
| // |
| //============================================================================ |
| |
| String scanOperator(sal_Unicode const *& rBegin, sal_Unicode const * pEnd) |
| { |
| sal_Unicode const * pTheBegin = rBegin; |
| while (rBegin != pEnd |
| && (INetMIME::isAlpha(*rBegin) || *rBegin == '!' |
| || *rBegin >= '<' && *rBegin <= '>')) |
| ++rBegin; |
| return String(pTheBegin, rBegin - pTheBegin); |
| } |
| |
| } |
| |
| //============================================================================ |
| // |
| // parseSearchArgument |
| // |
| //============================================================================ |
| |
| bool parseSearchArgument(String const & rInput, ucb::SearchInfo & rInfo) |
| { |
| /* Format of rInput: |
| |
| argument = *option [criterium *("OR" criterium)] |
| |
| option = ("--RECURSE" "=" ("NONE" / "ONE" / "DEEP")) |
| / (("--BASE" / "--FOLDERVIEW" / "--DOCVIEW" |
| / "--INDIRECT") |
| "=" bool) |
| |
| criterium = "EMPTY" / (term *("AND" term)) |
| |
| term = text-term / date-term / numeric-term / bool-term |
| |
| text-term = property ("CONT" / "!CONT" / ">=" / "<=" / "==" / "!=") |
| string *("-C" / "-R") |
| |
| date-term = property |
| (((">=" / "<=" / "==" / "!=") date) |
| / (("OLDER" / "YOUNGER") number)) |
| |
| numeric-term = property (">=" / "<=" / "==" / "!=") number |
| |
| bool-term = property ("TRUE" / "FALSE") |
| |
| property = 1*VCHAR |
| |
| string = DQUOTE |
| *(<any Unicode code point except DQUOTE or "\"> |
| / ("\" %x75 4HEXDIG) ; \uHHHH |
| / ("\" (DQUOTE / "\"))) |
| DQUOTE |
| |
| date = 1*2DIGIT "/" 1*2DIGIT "/" 4DIGIT ; mm/dd/yyyy |
| |
| number = ["+" / "-"] 1*DIGIT |
| */ |
| |
| sal_Unicode const * p = rInput.GetBuffer(); |
| sal_Unicode const * pEnd = p + rInput.Len(); |
| |
| // Parse options: |
| rInfo.Recursion = ucb::SearchRecursion_ONE_LEVEL; |
| rInfo.IncludeBase = true; |
| rInfo.RespectFolderViewRestrictions = true; |
| rInfo.RespectDocViewRestrictions = false; |
| rInfo.FollowIndirections = false; |
| enum OptionID { OPT_RECURSE, OPT_BASE, OPT_FOLDERVIEW, OPT_DOCVIEW, |
| OPT_INDIRECT, OPT_Count }; |
| struct OptionInfo |
| { |
| bool m_bSpecified; |
| sal_Bool * m_pValue; |
| }; |
| OptionInfo aOptions[OPT_Count]; |
| aOptions[OPT_RECURSE].m_bSpecified = false; |
| aOptions[OPT_RECURSE].m_pValue = 0; |
| aOptions[OPT_BASE].m_bSpecified = false; |
| aOptions[OPT_BASE].m_pValue = &rInfo.IncludeBase; |
| aOptions[OPT_FOLDERVIEW].m_bSpecified = false; |
| aOptions[OPT_FOLDERVIEW].m_pValue |
| = &rInfo.RespectFolderViewRestrictions; |
| aOptions[OPT_DOCVIEW].m_bSpecified = false; |
| aOptions[OPT_DOCVIEW].m_pValue = &rInfo.RespectDocViewRestrictions; |
| aOptions[OPT_INDIRECT].m_bSpecified = false; |
| aOptions[OPT_INDIRECT].m_pValue = &rInfo.FollowIndirections; |
| while (p != pEnd) |
| { |
| sal_Unicode const * q = p; |
| |
| skipWhiteSpace(q, pEnd); |
| if (pEnd - q < 2 || *q++ != '-' || *q++ != '-') |
| break; |
| String aOption(scanAtom(q, pEnd)); |
| OptionID eID; |
| if (aOption.EqualsIgnoreCaseAscii("recurse")) |
| eID = OPT_RECURSE; |
| else if (aOption.EqualsIgnoreCaseAscii("base")) |
| eID = OPT_BASE; |
| else if (aOption.EqualsIgnoreCaseAscii("folderview")) |
| eID = OPT_FOLDERVIEW; |
| else if (aOption.EqualsIgnoreCaseAscii("docview")) |
| eID = OPT_DOCVIEW; |
| else if (aOption.EqualsIgnoreCaseAscii("indirect")) |
| eID = OPT_INDIRECT; |
| else |
| break; |
| |
| if (aOptions[eID].m_bSpecified) |
| break; |
| aOptions[eID].m_bSpecified = true; |
| |
| skipWhiteSpace(q, pEnd); |
| if (q == pEnd || *q++ != '=') |
| break; |
| |
| skipWhiteSpace(q, pEnd); |
| String aValue(scanAtom(q, pEnd)); |
| if (eID == OPT_RECURSE) |
| { |
| if (aValue.EqualsIgnoreCaseAscii("none")) |
| rInfo.Recursion = ucb::SearchRecursion_NONE; |
| else if (aValue.EqualsIgnoreCaseAscii("one")) |
| rInfo.Recursion = ucb::SearchRecursion_ONE_LEVEL; |
| else if (aValue.EqualsIgnoreCaseAscii("deep")) |
| rInfo.Recursion = ucb::SearchRecursion_DEEP; |
| else |
| break; |
| } |
| else if (aValue.EqualsIgnoreCaseAscii("true")) |
| *aOptions[eID].m_pValue = true; |
| else if (aValue.EqualsIgnoreCaseAscii("false")) |
| *aOptions[eID].m_pValue = false; |
| else |
| break; |
| |
| p = q; |
| } |
| |
| // Parse criteria: |
| ucb::SearchCriterium aCriterium; |
| for (;;) |
| { |
| sal_Unicode const * q = p; |
| |
| // Parse either property name or "empty": |
| skipWhiteSpace(q, pEnd); |
| String aProperty(scanProperty(q, pEnd)); |
| sal_Unicode const * pPropertyEnd = q; |
| |
| // Parse operator: |
| skipWhiteSpace(q, pEnd); |
| String aOperator(scanOperator(q, pEnd)); |
| struct Operator |
| { |
| sal_Char const * m_pName; |
| sal_Int16 m_nText; |
| sal_Int16 m_nDate; |
| sal_Int16 m_nNumeric; |
| sal_Int16 m_nBool; |
| }; |
| static Operator const aOperators[] |
| = { { "cont", ucb::RuleOperator::CONTAINS, 0, 0, 0 }, |
| { "!cont", ucb::RuleOperator::CONTAINSNOT, 0, 0, 0 }, |
| { ">=", ucb::RuleOperator::GREATEREQUAL, |
| ucb::RuleOperator::GREATEREQUAL, |
| ucb::RuleOperator::GREATEREQUAL, 0 }, |
| { "<=", ucb::RuleOperator::LESSEQUAL, |
| ucb::RuleOperator::LESSEQUAL, ucb::RuleOperator::LESSEQUAL, |
| 0 }, |
| { "==", ucb::RuleOperator::EQUAL, ucb::RuleOperator::EQUAL, |
| ucb::RuleOperator::EQUAL, 0 }, |
| { "!=", ucb::RuleOperator::NOTEQUAL, |
| ucb::RuleOperator::NOTEQUAL, ucb::RuleOperator::NOTEQUAL, |
| 0 }, |
| { "true", 0, 0, 0, ucb::RuleOperator::VALUE_TRUE }, |
| { "false", 0, 0, 0, ucb::RuleOperator::VALUE_FALSE } }; |
| int const nOperatorCount = sizeof aOperators / sizeof (Operator); |
| Operator const * pTheOperator = 0; |
| for (int i = 0; i < nOperatorCount; ++i) |
| if (aOperator.EqualsIgnoreCaseAscii(aOperators[i].m_pName)) |
| { |
| pTheOperator = aOperators + i; |
| break; |
| } |
| bool bTerm = pTheOperator != 0; |
| |
| sal_Int16 nOperatorID; |
| uno::Any aTheOperand; |
| bool bCaseSensitive = false; |
| bool bRegularExpression = false; |
| if (bTerm) |
| { |
| skipWhiteSpace(q, pEnd); |
| bool bHasOperand = false; |
| |
| // Parse string operand: |
| if (!bHasOperand && pTheOperator->m_nText) |
| { |
| if (q != pEnd && *q == '"') |
| { |
| String aString; |
| for (sal_Unicode const * r = q + 1;;) |
| { |
| if (r == pEnd) |
| break; |
| sal_Unicode c = *r++; |
| if (c == '"') |
| { |
| bHasOperand = true; |
| aTheOperand <<= rtl::OUString(aString); |
| nOperatorID = pTheOperator->m_nText; |
| q = r; |
| break; |
| } |
| if (c == '\\') |
| { |
| if (r == pEnd) |
| break; |
| c = *r++; |
| if (c == 'u') |
| { |
| if (pEnd - r < 4) |
| break; |
| c = 0; |
| bool bBad = false; |
| for (int i = 0; i < 4; ++i) |
| { |
| int nWeight |
| = INetMIME::getHexWeight(*r++); |
| if (nWeight < 0) |
| { |
| bBad = false; |
| break; |
| } |
| c = sal_Unicode(c << 4 | nWeight); |
| } |
| if (bBad) |
| break; |
| } |
| else if (c != '"' && c != '\\') |
| break; |
| } |
| aString += c; |
| } |
| } |
| |
| // Parse "-C" and "-R": |
| if (bHasOperand) |
| for (;;) |
| { |
| skipWhiteSpace(q, pEnd); |
| if (pEnd - q >= 2 && q[0] == '-' |
| && (q[1] == 'C' || q[1] == 'c') |
| && !bCaseSensitive) |
| { |
| bCaseSensitive = true; |
| q += 2; |
| } |
| else if (pEnd - q >= 2 && q[0] == '-' |
| && (q[1] == 'R' || q[1] == 'r') |
| && !bRegularExpression) |
| { |
| bRegularExpression = true; |
| q += 2; |
| } |
| else |
| break; |
| } |
| } |
| |
| // Parse date operand: |
| if (!bHasOperand && pTheOperator->m_nDate != 0) |
| { |
| sal_Unicode const * r = q; |
| bool bOK = true; |
| USHORT nMonth = 0; |
| if (bOK && r != pEnd && INetMIME::isDigit(*r)) |
| nMonth = INetMIME::getWeight(*r++); |
| else |
| bOK = false; |
| if (bOK && r != pEnd && INetMIME::isDigit(*r)) |
| nMonth = 10 * nMonth + INetMIME::getWeight(*r++); |
| if (!(bOK && r != pEnd && *r++ == '/')) |
| bOK = false; |
| USHORT nDay = 0; |
| if (bOK && r != pEnd && INetMIME::isDigit(*r)) |
| nDay = INetMIME::getWeight(*r++); |
| else |
| bOK = false; |
| if (bOK && r != pEnd && INetMIME::isDigit(*r)) |
| nDay = 10 * nDay + INetMIME::getWeight(*r++); |
| if (!(bOK && r != pEnd && *r++ == '/')) |
| bOK = false; |
| USHORT nYear = 0; |
| for (int i = 0; bOK && i < 4; ++i) |
| if (r != pEnd && INetMIME::isDigit(*r)) |
| nYear = 10 * nYear + INetMIME::getWeight(*r++); |
| else |
| bOK = false; |
| if (bOK && Date(nDay, nMonth, nYear).IsValid()) |
| { |
| bHasOperand = true; |
| aTheOperand <<= util::Date(nDay, nMonth, nYear); |
| nOperatorID = pTheOperator->m_nDate; |
| q = r; |
| } |
| } |
| |
| // Parse number operand: |
| if (!bHasOperand && pTheOperator->m_nNumeric != 0) |
| { |
| sal_Unicode const * r = q; |
| bool bNegative = false; |
| if (*r == '+') |
| ++r; |
| else if (*r == '-') |
| { |
| bNegative = true; |
| ++r; |
| } |
| sal_Int64 nNumber = 0; |
| bool bDigits = false; |
| while (r != pEnd && INetMIME::isDigit(*r)) |
| { |
| nNumber = 10 * nNumber + INetMIME::getWeight(*r++); |
| if (nNumber > std::numeric_limits< sal_Int32 >::max()) |
| { |
| bDigits = false; |
| break; |
| } |
| } |
| if (bDigits) |
| { |
| bHasOperand = true; |
| aTheOperand |
| <<= sal_Int32(bNegative ? -sal_Int32(nNumber) : |
| sal_Int32(nNumber)); |
| nOperatorID = pTheOperator->m_nNumeric; |
| q = r; |
| } |
| } |
| |
| // Bool operator has no operand: |
| if (!bHasOperand && pTheOperator->m_nBool != 0) |
| { |
| bHasOperand = true; |
| nOperatorID = pTheOperator->m_nBool; |
| } |
| |
| bTerm = bHasOperand; |
| } |
| |
| bool bEmpty = false; |
| if (bTerm) |
| { |
| aCriterium.Terms.realloc(aCriterium.Terms.getLength() + 1); |
| aCriterium.Terms[aCriterium.Terms.getLength() - 1] |
| = ucb::RuleTerm(aProperty, aTheOperand, nOperatorID, |
| bCaseSensitive, bRegularExpression); |
| } |
| else if (aCriterium.Terms.getLength() == 0 |
| && aProperty.EqualsIgnoreCaseAscii("empty")) |
| { |
| bEmpty = true; |
| q = pPropertyEnd; |
| } |
| |
| if (!(bTerm || bEmpty)) |
| break; |
| |
| p = q; |
| skipWhiteSpace(p, pEnd); |
| |
| q = p; |
| String aConnection(scanAtom(q, pEnd)); |
| if (p == pEnd || aConnection.EqualsIgnoreCaseAscii("or")) |
| { |
| rInfo.Criteria.realloc(rInfo.Criteria.getLength() + 1); |
| rInfo.Criteria[rInfo.Criteria.getLength() - 1] = aCriterium; |
| aCriterium = ucb::SearchCriterium(); |
| p = q; |
| } |
| else if (bTerm && aConnection.EqualsIgnoreCaseAscii("and")) |
| p = q; |
| else |
| break; |
| } |
| |
| skipWhiteSpace(p, pEnd); |
| return p == pEnd; |
| } |
| |