xref: /trunk/main/ucb/workben/ucb/srcharg.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_ucb.hxx"
30 
31 #include <limits>
32 #include <com/sun/star/ucb/RuleOperator.hpp>
33 #include <com/sun/star/ucb/SearchInfo.hpp>
34 #include <com/sun/star/util/Date.hpp>
35 #include <tools/date.hxx>
36 #include <tools/inetmime.hxx>
37 #include <tools/string.hxx>
38 
39 #ifndef CHAOS_UCBDEMO_SRCHARG_HXX
40 #include <srcharg.hxx>
41 #endif
42 
43 namespace unnamed_chaos_ucbdemo_srcharg {}
44 using namespace unnamed_chaos_ucbdemo_srcharg;
45 	// unnamed namespaces don't work well yet...
46 
47 using namespace com::sun::star;
48 
49 //============================================================================
50 //
51 //  skipWhiteSpace
52 //
53 //============================================================================
54 
55 namespace unnamed_chaos_ucbdemo_srcharg {
56 
57 void skipWhiteSpace(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
58 {
59 	while (rBegin != pEnd
60 		   && (*rBegin == '\n' || *rBegin == '\t' || *rBegin == ' '))
61 		++rBegin;
62 }
63 
64 //============================================================================
65 //
66 //  scanAtom
67 //
68 //============================================================================
69 
70 String scanAtom(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
71 {
72 	sal_Unicode const * pTheBegin = rBegin;
73 	while (rBegin != pEnd && INetMIME::isAlpha(*rBegin))
74 		++rBegin;
75 	return String(pTheBegin, rBegin - pTheBegin);
76 }
77 
78 //============================================================================
79 //
80 //  scanProperty
81 //
82 //============================================================================
83 
84 String scanProperty(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
85 {
86 	sal_Unicode const * pTheBegin = rBegin;
87 	while (rBegin != pEnd
88 		   && !(*rBegin == '\n' || *rBegin == '\t' || *rBegin == ' '))
89 		++rBegin;
90 	return String(pTheBegin, rBegin - pTheBegin);
91 }
92 
93 //============================================================================
94 //
95 //  scanOperator
96 //
97 //============================================================================
98 
99 String scanOperator(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
100 {
101 	sal_Unicode const * pTheBegin = rBegin;
102 	while (rBegin != pEnd
103 		   && (INetMIME::isAlpha(*rBegin) || *rBegin == '!'
104 			   || *rBegin >= '<' && *rBegin <= '>'))
105 		++rBegin;
106 	return String(pTheBegin, rBegin - pTheBegin);
107 }
108 
109 }
110 
111 //============================================================================
112 //
113 //  parseSearchArgument
114 //
115 //============================================================================
116 
117 bool parseSearchArgument(String const & rInput, ucb::SearchInfo & rInfo)
118 {
119 	/* Format of rInput:
120 
121 	   argument = *option [criterium *("OR" criterium)]
122 
123 	   option = ("--RECURSE" "=" ("NONE" / "ONE" / "DEEP"))
124 	                / (("--BASE" / "--FOLDERVIEW" / "--DOCVIEW"
125 					            / "--INDIRECT")
126 					       "=" bool)
127 
128 	   criterium = "EMPTY" / (term *("AND" term))
129 
130 	   term = text-term / date-term / numeric-term / bool-term
131 
132 	   text-term = property ("CONT" / "!CONT" / ">=" / "<=" / "==" / "!=")
133 	                   string *("-C" / "-R")
134 
135 	   date-term = property
136 	                   (((">=" / "<=" / "==" / "!=") date)
137 					        / (("OLDER" / "YOUNGER") number))
138 
139 	   numeric-term = property (">=" / "<=" / "==" / "!=") number
140 
141 	   bool-term = property ("TRUE" / "FALSE")
142 
143 	   property = 1*VCHAR
144 
145 	   string = DQUOTE
146 	                *(<any Unicode code point except DQUOTE or "\">
147 					      / ("\" %x75 4HEXDIG)  ; \uHHHH
148 					      / ("\" (DQUOTE / "\")))
149 					DQUOTE
150 
151 	   date = 1*2DIGIT "/" 1*2DIGIT "/" 4DIGIT  ; mm/dd/yyyy
152 
153 	   number = ["+" / "-"] 1*DIGIT
154 	*/
155 
156 	sal_Unicode const * p = rInput.GetBuffer();
157 	sal_Unicode const * pEnd = p + rInput.Len();
158 
159 	// Parse options:
160 	rInfo.Recursion = ucb::SearchRecursion_ONE_LEVEL;
161 	rInfo.IncludeBase = true;
162 	rInfo.RespectFolderViewRestrictions = true;
163 	rInfo.RespectDocViewRestrictions = false;
164 	rInfo.FollowIndirections = false;
165 	enum OptionID { OPT_RECURSE, OPT_BASE, OPT_FOLDERVIEW, OPT_DOCVIEW,
166 					OPT_INDIRECT, OPT_Count };
167 	struct OptionInfo
168 	{
169 		bool m_bSpecified;
170 		sal_Bool * m_pValue;
171 	};
172 	OptionInfo aOptions[OPT_Count];
173 	aOptions[OPT_RECURSE].m_bSpecified = false;
174 	aOptions[OPT_RECURSE].m_pValue = 0;
175 	aOptions[OPT_BASE].m_bSpecified = false;
176 	aOptions[OPT_BASE].m_pValue = &rInfo.IncludeBase;
177 	aOptions[OPT_FOLDERVIEW].m_bSpecified = false;
178 	aOptions[OPT_FOLDERVIEW].m_pValue
179 		= &rInfo.RespectFolderViewRestrictions;
180 	aOptions[OPT_DOCVIEW].m_bSpecified = false;
181 	aOptions[OPT_DOCVIEW].m_pValue = &rInfo.RespectDocViewRestrictions;
182 	aOptions[OPT_INDIRECT].m_bSpecified = false;
183 	aOptions[OPT_INDIRECT].m_pValue = &rInfo.FollowIndirections;
184 	while (p != pEnd)
185 	{
186 		sal_Unicode const * q = p;
187 
188 		skipWhiteSpace(q, pEnd);
189 		if (pEnd - q < 2 || *q++ != '-' || *q++ != '-')
190 			break;
191 		String aOption(scanAtom(q, pEnd));
192 		OptionID eID;
193 		if (aOption.EqualsIgnoreCaseAscii("recurse"))
194 			eID = OPT_RECURSE;
195 		else if (aOption.EqualsIgnoreCaseAscii("base"))
196 			eID = OPT_BASE;
197 		else if (aOption.EqualsIgnoreCaseAscii("folderview"))
198 			eID = OPT_FOLDERVIEW;
199 		else if (aOption.EqualsIgnoreCaseAscii("docview"))
200 			eID = OPT_DOCVIEW;
201 		else if (aOption.EqualsIgnoreCaseAscii("indirect"))
202 			eID = OPT_INDIRECT;
203 		else
204 			break;
205 
206 		if (aOptions[eID].m_bSpecified)
207 			break;
208 		aOptions[eID].m_bSpecified = true;
209 
210 		skipWhiteSpace(q, pEnd);
211 		if (q == pEnd || *q++ != '=')
212 			break;
213 
214 		skipWhiteSpace(q, pEnd);
215 		String aValue(scanAtom(q, pEnd));
216 		if (eID == OPT_RECURSE)
217 		{
218 			if (aValue.EqualsIgnoreCaseAscii("none"))
219 				rInfo.Recursion = ucb::SearchRecursion_NONE;
220 			else if (aValue.EqualsIgnoreCaseAscii("one"))
221 				rInfo.Recursion = ucb::SearchRecursion_ONE_LEVEL;
222 			else if (aValue.EqualsIgnoreCaseAscii("deep"))
223 				rInfo.Recursion = ucb::SearchRecursion_DEEP;
224 			else
225 				break;
226 		}
227 		else if (aValue.EqualsIgnoreCaseAscii("true"))
228 			*aOptions[eID].m_pValue = true;
229 		else if (aValue.EqualsIgnoreCaseAscii("false"))
230 			*aOptions[eID].m_pValue = false;
231 		else
232 			break;
233 
234 		p = q;
235 	}
236 
237 	// Parse criteria:
238 	ucb::SearchCriterium aCriterium;
239 	for (;;)
240 	{
241 		sal_Unicode const * q = p;
242 
243 		// Parse either property name or "empty":
244 		skipWhiteSpace(q, pEnd);
245 		String aProperty(scanProperty(q, pEnd));
246 		sal_Unicode const * pPropertyEnd = q;
247 
248 		// Parse operator:
249 		skipWhiteSpace(q, pEnd);
250 		String aOperator(scanOperator(q, pEnd));
251 		struct Operator
252 		{
253 			sal_Char const * m_pName;
254 			sal_Int16 m_nText;
255 			sal_Int16 m_nDate;
256 			sal_Int16 m_nNumeric;
257 			sal_Int16 m_nBool;
258 		};
259 		static Operator const aOperators[]
260 			= { { "cont", ucb::RuleOperator::CONTAINS, 0, 0, 0 },
261 				{ "!cont", ucb::RuleOperator::CONTAINSNOT, 0, 0, 0 },
262 				{ ">=", ucb::RuleOperator::GREATEREQUAL,
263 				  ucb::RuleOperator::GREATEREQUAL,
264 				  ucb::RuleOperator::GREATEREQUAL, 0 },
265 				{ "<=", ucb::RuleOperator::LESSEQUAL,
266 				  ucb::RuleOperator::LESSEQUAL, ucb::RuleOperator::LESSEQUAL,
267 				  0 },
268 				{ "==", ucb::RuleOperator::EQUAL, ucb::RuleOperator::EQUAL,
269 				  ucb::RuleOperator::EQUAL, 0 },
270 				{ "!=", ucb::RuleOperator::NOTEQUAL,
271 				  ucb::RuleOperator::NOTEQUAL, ucb::RuleOperator::NOTEQUAL,
272 				  0 },
273 				{ "true", 0, 0, 0, ucb::RuleOperator::VALUE_TRUE },
274 				{ "false", 0, 0, 0, ucb::RuleOperator::VALUE_FALSE } };
275 		int const nOperatorCount = sizeof aOperators / sizeof (Operator);
276 		Operator const * pTheOperator = 0;
277 		for (int i = 0; i < nOperatorCount; ++i)
278 			if (aOperator.EqualsIgnoreCaseAscii(aOperators[i].m_pName))
279 			{
280 				pTheOperator = aOperators + i;
281 				break;
282 			}
283 		bool bTerm = pTheOperator != 0;
284 
285 		sal_Int16 nOperatorID;
286 		uno::Any aTheOperand;
287 		bool bCaseSensitive = false;
288 		bool bRegularExpression = false;
289 		if (bTerm)
290 		{
291 			skipWhiteSpace(q, pEnd);
292 			bool bHasOperand = false;
293 
294 			// Parse string operand:
295 			if (!bHasOperand && pTheOperator->m_nText)
296 			{
297 				if (q != pEnd && *q == '"')
298 				{
299 					String aString;
300 					for (sal_Unicode const * r = q + 1;;)
301 					{
302 						if (r == pEnd)
303 							break;
304 						sal_Unicode c = *r++;
305 						if (c == '"')
306 						{
307 							bHasOperand = true;
308 							aTheOperand <<= rtl::OUString(aString);
309 							nOperatorID = pTheOperator->m_nText;
310 							q = r;
311 							break;
312 						}
313 						if (c == '\\')
314 						{
315 							if (r == pEnd)
316 								break;
317 							c = *r++;
318 							if (c == 'u')
319 							{
320 								if (pEnd - r < 4)
321 									break;
322 								c = 0;
323 								bool bBad = false;
324 								for (int i = 0; i < 4; ++i)
325 								{
326 									int nWeight
327 										= INetMIME::getHexWeight(*r++);
328 									if (nWeight < 0)
329 									{
330 										bBad = false;
331 										break;
332 									}
333 									c = sal_Unicode(c << 4 | nWeight);
334 								}
335 								if (bBad)
336 									break;
337 							}
338 							else if (c != '"' && c != '\\')
339 								break;
340 						}
341 						aString += c;
342 					}
343 				}
344 
345 				// Parse "-C" and "-R":
346 				if (bHasOperand)
347 					for (;;)
348 					{
349 						skipWhiteSpace(q, pEnd);
350 						if (pEnd - q >= 2 && q[0] == '-'
351 							&& (q[1] == 'C' || q[1] == 'c')
352 							&& !bCaseSensitive)
353 						{
354 							bCaseSensitive = true;
355 							q += 2;
356 						}
357 						else if (pEnd - q >= 2 && q[0] == '-'
358 								 && (q[1] == 'R' || q[1] == 'r')
359 								 && !bRegularExpression)
360 						{
361 							bRegularExpression = true;
362 							q += 2;
363 						}
364 						else
365 							break;
366 					}
367 			}
368 
369 			// Parse date operand:
370 			if (!bHasOperand && pTheOperator->m_nDate != 0)
371 			{
372 				sal_Unicode const * r = q;
373 				bool bOK = true;
374 				USHORT nMonth = 0;
375 				if (bOK && r != pEnd && INetMIME::isDigit(*r))
376 					nMonth = INetMIME::getWeight(*r++);
377 				else
378 					bOK = false;
379 				if (bOK && r != pEnd && INetMIME::isDigit(*r))
380 					nMonth = 10 * nMonth + INetMIME::getWeight(*r++);
381 				if (!(bOK && r != pEnd && *r++ == '/'))
382 					bOK = false;
383 				USHORT nDay = 0;
384 				if (bOK && r != pEnd && INetMIME::isDigit(*r))
385 					nDay = INetMIME::getWeight(*r++);
386 				else
387 					bOK = false;
388 				if (bOK && r != pEnd && INetMIME::isDigit(*r))
389 					nDay = 10 * nDay + INetMIME::getWeight(*r++);
390 				if (!(bOK && r != pEnd && *r++ == '/'))
391 					bOK = false;
392 				USHORT nYear = 0;
393 				for (int i = 0; bOK && i < 4; ++i)
394 					if (r != pEnd && INetMIME::isDigit(*r))
395 						nYear = 10 * nYear + INetMIME::getWeight(*r++);
396 					else
397 						bOK = false;
398 				if (bOK && Date(nDay, nMonth, nYear).IsValid())
399 				{
400 					bHasOperand = true;
401 					aTheOperand <<= util::Date(nDay, nMonth, nYear);
402 					nOperatorID = pTheOperator->m_nDate;
403 					q = r;
404 				}
405 			}
406 
407 			// Parse number operand:
408 			if (!bHasOperand && pTheOperator->m_nNumeric != 0)
409 			{
410 				sal_Unicode const * r = q;
411 				bool bNegative = false;
412 				if (*r == '+')
413 					++r;
414 				else if (*r == '-')
415 				{
416 					bNegative = true;
417 					++r;
418 				}
419 				sal_Int64 nNumber = 0;
420 				bool bDigits = false;
421 				while (r != pEnd && INetMIME::isDigit(*r))
422 				{
423 					nNumber = 10 * nNumber + INetMIME::getWeight(*r++);
424 					if (nNumber > std::numeric_limits< sal_Int32 >::max())
425 					{
426 						bDigits = false;
427 						break;
428 					}
429 				}
430 				if (bDigits)
431 				{
432 					bHasOperand = true;
433 					aTheOperand
434 						<<= sal_Int32(bNegative ? -sal_Int32(nNumber) :
435 									              sal_Int32(nNumber));
436 					nOperatorID = pTheOperator->m_nNumeric;
437 					q = r;
438 				}
439 			}
440 
441 			// Bool operator has no operand:
442 			if (!bHasOperand && pTheOperator->m_nBool != 0)
443 			{
444 				bHasOperand = true;
445 				nOperatorID = pTheOperator->m_nBool;
446 			}
447 
448 			bTerm = bHasOperand;
449 		}
450 
451 		bool bEmpty = false;
452 		if (bTerm)
453 		{
454 			aCriterium.Terms.realloc(aCriterium.Terms.getLength() + 1);
455 			aCriterium.Terms[aCriterium.Terms.getLength() - 1]
456 				= ucb::RuleTerm(aProperty, aTheOperand, nOperatorID,
457 								bCaseSensitive, bRegularExpression);
458 		}
459 		else if (aCriterium.Terms.getLength() == 0
460 				 && aProperty.EqualsIgnoreCaseAscii("empty"))
461 		{
462 			bEmpty = true;
463 			q = pPropertyEnd;
464 		}
465 
466 		if (!(bTerm || bEmpty))
467 			break;
468 
469 		p = q;
470 		skipWhiteSpace(p, pEnd);
471 
472 		q = p;
473 		String aConnection(scanAtom(q, pEnd));
474 		if (p == pEnd || aConnection.EqualsIgnoreCaseAscii("or"))
475 		{
476 			rInfo.Criteria.realloc(rInfo.Criteria.getLength() + 1);
477 			rInfo.Criteria[rInfo.Criteria.getLength() - 1] = aCriterium;
478 			aCriterium = ucb::SearchCriterium();
479 			p = q;
480 		}
481 		else if (bTerm && aConnection.EqualsIgnoreCaseAscii("and"))
482 			p = q;
483 		else
484 			break;
485 	}
486 
487 	skipWhiteSpace(p, pEnd);
488 	return p == pEnd;
489 }
490 
491