Author: lgross
Date: Thu Jul 17 12:55:59 2014
New Revision: 1611348

URL: http://svn.apache.org/r1611348
Log:
Added CMISQueryStatement to create proper escaped SQL statements

Added:
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.h
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.m
Modified:
    chemistry/objectivecmis/trunk/ObjectiveCMIS.xcodeproj/project.pbxproj
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.h
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m
    chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS.xcodeproj/project.pbxproj
URL: 
http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS.xcodeproj/project.pbxproj?rev=1611348&r1=1611347&r2=1611348&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS.xcodeproj/project.pbxproj 
(original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS.xcodeproj/project.pbxproj Thu 
Jul 17 12:55:59 2014
@@ -21,6 +21,9 @@
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
+               253F2D2F19741FAE006BA517 /* CMISQueryStatement.h in Headers */ 
= {isa = PBXBuildFile; fileRef = 253F2D2D19741FAE006BA517 /* 
CMISQueryStatement.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               253F2D3019741FAE006BA517 /* CMISQueryStatement.m in Sources */ 
= {isa = PBXBuildFile; fileRef = 253F2D2E19741FAE006BA517 /* 
CMISQueryStatement.m */; };
+               255E7EE41975069500C683A0 /* CMISQueryStatement.m in Sources */ 
= {isa = PBXBuildFile; fileRef = 253F2D2E19741FAE006BA517 /* 
CMISQueryStatement.m */; };
                258998DB18D73D5A0091BA96 /* CMISAce.h in Headers */ = {isa = 
PBXBuildFile; fileRef = 258998D718D73D5A0091BA96 /* CMISAce.h */; settings = 
{ATTRIBUTES = (Public, ); }; };
                258998DC18D73D5A0091BA96 /* CMISAce.m in Sources */ = {isa = 
PBXBuildFile; fileRef = 258998D818D73D5A0091BA96 /* CMISAce.m */; };
                258998DD18D73D5A0091BA96 /* CMISAcl.h in Headers */ = {isa = 
PBXBuildFile; fileRef = 258998D918D73D5A0091BA96 /* CMISAcl.h */; settings = 
{ATTRIBUTES = (Public, ); }; };
@@ -278,6 +281,8 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
+               253F2D2D19741FAE006BA517 /* CMISQueryStatement.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = 
CMISQueryStatement.h; path = Bindings/CMISQueryStatement.h; sourceTree = 
"<group>"; };
+               253F2D2E19741FAE006BA517 /* CMISQueryStatement.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name 
= CMISQueryStatement.m; path = Bindings/CMISQueryStatement.m; sourceTree = 
"<group>"; };
                258998D718D73D5A0091BA96 /* CMISAce.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = 
CMISAce.h; path = Common/CMISAce.h; sourceTree = "<group>"; };
                258998D818D73D5A0091BA96 /* CMISAce.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name 
= CMISAce.m; path = Common/CMISAce.m; sourceTree = "<group>"; };
                258998D918D73D5A0091BA96 /* CMISAcl.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = 
CMISAcl.h; path = Common/CMISAcl.h; sourceTree = "<group>"; };
@@ -761,6 +766,8 @@
                828072D315153EC100EF635C /* Bindings */ = {
                        isa = PBXGroup;
                        children = (
+                               8280730D1515405C00EF635C /* AtomPub */,
+                               5892CBE0192CB83800C7734A /* Browser */,
                                82AD4AEE15416A150012DDB6 /* CMISAclService.h */,
                                828073191515405C00EF635C /* CMISBinding.h */,
                                82AD4AE9154164290012DDB6 /* 
CMISBindingFactory.h */,
@@ -786,11 +793,13 @@
                                FE417D6015761A34009056AA /* 
CMISPropertyDefinition.m */,
                                4EA61BDF1564F73900C759E4 /* CMISQueryResult.h 
*/,
                                4EA61BE01564F73900C759E4 /* CMISQueryResult.m 
*/,
+                               253F2D2D19741FAE006BA517 /* 
CMISQueryStatement.h */,
+                               253F2D2E19741FAE006BA517 /* 
CMISQueryStatement.m */,
                                82AD4AF715416AC10012DDB6 /* 
CMISRelationshipService.h */,
                                5892CB7A192CB65D00C7734A /* 
CMISRelationshipTypeDefinition.h */,
                                5892CB7B192CB65D00C7734A /* 
CMISRelationshipTypeDefinition.m */,
-                               FE417D6815761A34009056C1 /* CMISRenditionData.m 
*/,
                                FE417D6815761A34009056BF /* CMISRenditionData.h 
*/,
+                               FE417D6815761A34009056C1 /* CMISRenditionData.m 
*/,
                                8276E157155E392A00344A29 /* 
CMISRepositoryService.h */,
                                5892CB7C192CB65D00C7734A /* 
CMISSecondaryTypeDefinition.h */,
                                5892CB7D192CB65D00C7734A /* 
CMISSecondaryTypeDefinition.m */,
@@ -799,8 +808,6 @@
                                5892CB7E192CB65D00C7734A /* 
CMISTypeDefinitionCache.h */,
                                5892CB7F192CB65D00C7734A /* 
CMISTypeDefinitionCache.m */,
                                8276E158155E392A00344A29 /* 
CMISVersioningService.h */,
-                               8280730D1515405C00EF635C /* AtomPub */,
-                               5892CBE0192CB83800C7734A /* Browser */,
                        );
                        name = Bindings;
                        sourceTree = "<group>";
@@ -919,6 +926,7 @@
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               253F2D2F19741FAE006BA517 /* 
CMISQueryStatement.h in Headers */,
                                828072E51515403800EF635C /* CMISCollection.h in 
Headers */,
                                828072E71515403800EF635C /* CMISDocument.h in 
Headers */,
                                5892CBC9192CB7D900C7734A /* 
CMISAtomPubExtensionDataParserBase.h in Headers */,
@@ -1194,6 +1202,7 @@
                                82C1C63715348EC4009B7B3D /* 
CMISAtomPubNavigationService.m in Sources */,
                                5892CB85192CB65D00C7734A /* 
CMISRelationshipTypeDefinition.m in Sources */,
                                5892CB95192CB73D00C7734A /* 
CMISAtomPubObjectByIdUriBuilder.m in Sources */,
+                               253F2D3019741FAE006BA517 /* 
CMISQueryStatement.m in Sources */,
                                5892CBC0192CB7D900C7734A /* 
CMISAtomFeedParser.m in Sources */,
                                5892CBBE192CB7D900C7734A /* 
CMISAtomEntryWriter.m in Sources */,
                                82AD4AEC1541642A0012DDB6 /* 
CMISBindingFactory.m in Sources */,
@@ -1268,6 +1277,7 @@
                        files = (
                                828072C415153DE900EF635C /* 
ObjectiveCMISTests.m in Sources */,
                                75E7789D155BA59D00191BAE /* 
ObjectiveCMISTests+Environment.m in Sources */,
+                               255E7EE41975069500C683A0 /* 
CMISQueryStatement.m in Sources */,
                                4EA61BD91564F70C00C759E4 /* 
CMISStringInOutParameter.m in Sources */,
                                4EA61BDC1564F70C00C759E4 /* CMISURLUtil.m in 
Sources */,
                                4EA61BE31564F73900C759E4 /* CMISObjectList.m in 
Sources */,

Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.h
URL: 
http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.h?rev=1611348&view=auto
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.h 
(added)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.h 
Thu Jul 17 12:55:59 2014
@@ -0,0 +1,170 @@
+/*
+ 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.
+ */
+
+#import <Foundation/Foundation.h>
+
+/**
+ * CMISQueryStatement.
+ * Sample code:
+ *
+ * CMISQueryStatement *qs =
+ *   [[CMISQueryStatement alloc] initWithStatement:@"SELECT ?, ? FROM ? WHERE 
? > ? AND IN_FOLDER(?) OR ? IN (?)"];
+ *
+ * [qs setPropertyAtIndex:1 property:@"cmis:document"];
+ * [qs setPropertyAtIndex:2 property:@"cmis:name"];
+ * [qs setTypeAtIndex:3 type:@"cmis:document"];
+ *
+ * [qs setPropertyAtIndex:4 property:@"cmis:creationDate];
+ * [qs setDateAtIndex:5 date:creationDate];
+ *
+ * [qs setStringAtIndex:6 string:cmisDocument.identifier];
+ *
+ * [qs setPropertyAtIndex:7 property:@"cmis:createdBy];
+ * [qs setStringAtIndex:4 string:@"8, bob, tom, lisa"];
+ *
+ * NSString *statement = [qs queryString];
+ */
+@interface CMISQueryStatement : NSObject
+
+/**
+ * Initialize Query Statement. Use ? to define placeholders
+ *
+ * @param statement 
+            THe SQL statement
+ */
+- (id)initWithStatement:(NSString*)statement;
+
+/**
+ * Sets the designated parameter to the query name of the given type.
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param type
+ *            the object type
+ */
+- (void)setTypeAtIndex:(NSUInteger)parameterIndex type:(NSString*)type;
+
+/**
+ * Sets the designated parameter to the query name of the given property.
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param propertyId
+ *            the property ID
+ */
+- (void)setPropertyAtIndex:(NSUInteger)parameterIndex 
property:(NSString*)property;
+
+/**
+ * Sets the designated parameter to the given string.
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param string
+ *            the string
+ */
+- (void)setStringAtIndex:(NSUInteger)parameterIndex string:(NSString*)string;
+
+/**
+ * Sets the designated parameter to the given string. It does not escape
+ * backslashes ('\') in front of '%' and '_'.
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param string
+ *            the LIKE string
+ */
+- (void)setStringLikeAtIndex:(NSUInteger)parameterIndex 
string:(NSString*)string;
+
+/**
+ * Sets the designated parameter to the given string in a CMIS contains
+ * statement.
+ * <p>
+ * Note that the CMIS specification requires two levels of escaping. The
+ * first level escapes ', ", \ characters to \', \" and \\. The characters
+ * *, ? and - are interpreted as text search operators and are not escaped
+ * on first level. If *, ?, - shall be used as literals, they must be passed
+ * escaped with \*, \? and \- to this method.
+ * <p>
+ * For all statements in a CONTAINS() clause it is required to isolate those
+ * from a query statement. Therefore a second level escaping is performed.
+ * On the second level grammar ", ', - and \ are escaped with a \. See the
+ * spec for further details.
+ * <p>
+ * Summary (input --> first level escaping --> second level escaping and
+ * output): * --> * --> * ? --> ? --> ? - --> - --> - \ --> \\ --> \\\\ (for
+ * any other character following other than * ? -) \* --> \* --> \\* \? -->
+ * \? --> \\? \- --> \- --> \\- ' --> \' --> \\\' " --> \" --> \\\"
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param string
+ *            the CONTAINS string
+ */
+- (void)setStringContainsAtIndex:(NSUInteger)parameterIndex 
string:(NSString*)string;
+
+/**
+ * Sets the designated parameter to the given number.
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param number
+ *            the number
+ */
+- (void)setNumberAtIndex:(NSUInteger)parameterIndex number:(NSNumber*)number;
+
+/**
+ * Sets the designated parameter to the given URL.
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param url
+ *            the URL
+ */
+- (void)setUrlAtIndex:(NSUInteger)parameterIndex url:(NSURL*)url;
+
+/**
+ * Sets the designated parameter to the given boolean.
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param boolean
+ *            the boolean
+ */
+- (void)setBooleanAtIndex:(NSUInteger)parameterIndex boolean:(BOOL)boolean;
+
+/**
+ * Sets the designated parameter to the given DateTime value with the prefix
+ * 'TIMESTAMP '.
+ *
+ * @param parameterIndex
+ *            the parameter index (one-based)
+ * @param date
+ *            the DateTime value as NSDate object
+ */
+- (void)setDateTimeAtIndex:(NSUInteger)parameterIndex date:(NSDate*)date;
+
+
+/**
+ * Returns the query statement.
+ *
+ * @return the query statement
+ */
+- (NSString*)queryString;
+
+@end

Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.m
URL: 
http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.m?rev=1611348&view=auto
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.m 
(added)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISQueryStatement.m 
Thu Jul 17 12:55:59 2014
@@ -0,0 +1,212 @@
+/*
+ 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.
+ */
+
+#import "CMISQueryStatement.h"
+
+@interface CMISQueryStatement ()
+
+@property (nonatomic, strong) NSString* statement;
+@property (nonatomic, strong) NSMutableDictionary *parametersDictionary;
+
+@end
+
+@implementation CMISQueryStatement
+
+- (id)initWithStatement:(NSString*)statement {
+    self = [super init];
+    if (self) {
+        self.statement = statement;
+        self.parametersDictionary = [NSMutableDictionary dictionary];
+    }
+    
+    return self;
+}
+
+- (void)setTypeAtIndex:(NSUInteger)parameterIndex type:(NSString*)type {
+    if (type && type.length > 0) {
+        [self.parametersDictionary setObject:[CMISQueryStatement 
escapeString:type withSurroundingQuotes:NO] forKey:[NSNumber 
numberWithInteger:parameterIndex]];
+    }
+}
+
+- (void)setPropertyAtIndex:(NSUInteger)parameterIndex 
property:(NSString*)property {
+    if (property && property.length > 0) {
+        [self.parametersDictionary setObject:[CMISQueryStatement 
escapeString:property withSurroundingQuotes:NO] forKey:[NSNumber 
numberWithInteger:parameterIndex]];
+    }
+}
+
+- (void)setNumberAtIndex:(NSUInteger)parameterIndex number:(NSNumber*)number {
+    if (number) {
+        [self.parametersDictionary setObject:number forKey:[NSNumber 
numberWithInteger:parameterIndex]];
+    }
+}
+
+- (void)setStringAtIndex:(NSUInteger)parameterIndex string:(NSString*)string {
+    if (string && string.length > 0) {
+        [self.parametersDictionary setObject:[CMISQueryStatement 
escapeString:string withSurroundingQuotes:YES] forKey:[NSNumber 
numberWithInteger:parameterIndex]];
+    }
+}
+
+- (void)setStringLikeAtIndex:(NSUInteger)parameterIndex 
string:(NSString*)string {
+    if (string && string.length > 0) {
+        [self.parametersDictionary setObject:[CMISQueryStatement 
escapeLike:string] forKey:[NSNumber numberWithInteger:parameterIndex]];
+    }
+}
+
+- (void)setStringContainsAtIndex:(NSUInteger)parameterIndex 
string:(NSString*)string {
+    if (string && string.length > 0) {
+        [self.parametersDictionary setObject:[CMISQueryStatement 
escapeContains:string] forKey:[NSNumber numberWithInteger:parameterIndex]];
+    }
+}
+
+- (void)setUrlAtIndex:(NSUInteger)parameterIndex url:(NSURL*)url {
+    if (url) {
+        NSError *error;
+        NSString *urlString = [NSString stringWithContentsOfURL:url 
encoding:NSUTF8StringEncoding error:&error];
+        if (!error && urlString && urlString.length >0) {
+            [self.parametersDictionary setObject:[CMISQueryStatement 
escapeString:urlString withSurroundingQuotes:YES] forKey:[NSNumber 
numberWithInteger:parameterIndex]];
+        }
+    }
+}
+
+- (void)setBooleanAtIndex:(NSUInteger)parameterIndex boolean:(BOOL)boolean {
+    NSString *booleanString;
+    if (boolean) {
+        booleanString = @"YES";
+    } else {
+        booleanString = @"NO";
+    }
+    [self.parametersDictionary setObject:booleanString forKey:[NSNumber 
numberWithInteger:parameterIndex]];
+}
+
+- (void)setDateTimeAtIndex:(NSUInteger)parameterIndex date:(NSDate*)date {
+    if (date) {
+        [self.parametersDictionary setObject:[NSString 
stringWithFormat:@"TIMESTAMP '%@'", [CMISQueryStatement convert:date]] 
forKey:[NSNumber numberWithInteger:parameterIndex]];
+    }
+}
+
+
+- (NSString*)queryString {
+    BOOL inStr = false;
+    NSUInteger parameterIndex = 0;
+    
+    NSMutableString *retStr = [NSMutableString string];
+    
+    for (NSUInteger i = 0; i < self.statement.length; i++) {
+        unichar c = [self.statement characterAtIndex:i];
+        
+        if (c == '\'') {
+            if (inStr && [retStr characterAtIndex:i - 1] == '\\') {
+                inStr = true;
+            } else {
+                inStr = !inStr;
+            }
+            [retStr appendString:[NSString stringWithCharacters:&c length:1]];
+        } else if (c == '?' && !inStr) {
+            parameterIndex++;
+            NSObject *parameter = [self.parametersDictionary 
objectForKey:[NSNumber numberWithInteger:parameterIndex]];
+            NSString *paramValue = nil;
+            if ([parameter isKindOfClass:NSString.class]) {
+                paramValue = (NSString*)parameter;
+            } else if ([parameter isKindOfClass:NSNumber.class]) {
+                paramValue = [(NSNumber*)parameter stringValue];
+            }
+            if (paramValue) {
+                // Replace placeholder
+                [retStr appendString:paramValue];
+            }
+        } else {
+            [retStr appendString:[NSString stringWithCharacters:&c length:1]];
+        }
+    }
+    
+    return retStr;
+}
+
+#pragma mark - Escaping methods
+
++ (NSString*)escapeString:(NSString*)string withSurroundingQuotes:(BOOL)quotes 
{
+    NSMutableString *escapedString = [NSMutableString string];
+    [escapedString appendString:quotes ? @"'" : @"" ];
+    for (NSUInteger i = 0; i < string.length; i++) {
+        unichar c = [string characterAtIndex:i];
+        
+        if (c == '\'' || c == '\\') {
+            [escapedString appendString:@"\\"];
+        }
+        
+        [escapedString appendString:[NSString stringWithCharacters:&c 
length:1]];
+    }
+    
+    if (quotes) {
+        [escapedString appendString:@"\'"];
+    }
+    
+    return escapedString;
+}
+
++ (NSString*)escapeLike:(NSString*)string {
+    NSMutableString *escapedString = [NSMutableString stringWithString:@"'"];
+    for (NSUInteger i = 0; i < string.length; i++) {
+        unichar c = [string characterAtIndex:i];
+        
+        if (c == '\'') {
+            [escapedString appendString:@"\\"];
+        } else if (c == '\\') {
+            if (i + 1 < string.length && ([string characterAtIndex:(i + 1)] == 
'%' || [string characterAtIndex:(i + 1)] == '_')) {
+                // no additional back slash
+            } else {
+                [escapedString appendString:@"\\"];
+            }
+        }
+        
+        [escapedString appendString:[NSString stringWithCharacters:&c 
length:1]];
+    }
+    
+    [escapedString appendString:@"\'"];
+    return escapedString;
+}
+
++ (NSString*)escapeContains:(NSString*)string {
+    NSMutableString *escapedString = [NSMutableString stringWithString:@"'"];
+    for (NSUInteger i = 0; i < string.length; i++) {
+        unichar c = [string characterAtIndex:i];
+        
+        if (c == '\\') {
+            [escapedString appendString:@"\\"];
+        } else if (c == '\'' || c == '\"') {
+            [escapedString appendString:@"\\\\\\"];
+        }
+        
+        [escapedString appendString:[NSString stringWithCharacters:&c 
length:1]];
+    }
+    
+    [escapedString appendString:@"\'"];
+    return escapedString;
+}
+
++ (NSString*)convert:(NSDate*)date {
+    NSDateFormatter* timeStampFormatter = [[NSDateFormatter alloc] init];
+    timeStampFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+    timeStampFormatter.calendar = [[NSCalendar alloc] 
initWithCalendarIdentifier:NSGregorianCalendar];
+    timeStampFormatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
+    
+    return [timeStampFormatter stringFromDate:date];
+}
+
+@end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.h
URL: 
http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.h?rev=1611348&r1=1611347&r2=1611348&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.h Thu Jul 17 
12:55:59 2014
@@ -22,6 +22,7 @@
 #import "CMISRepositoryInfo.h"
 #import "CMISBinding.h"
 #import "CMISFolder.h"
+#import "CMISQueryStatement.h"
 
 @class CMISOperationContext;
 @class CMISPagedResult;
@@ -114,14 +115,14 @@
 - (CMISRequest*)retrieveTypeDefinition:(NSString *)typeId 
                completionBlock:(void (^)(CMISTypeDefinition *typeDefinition, 
NSError *error))completionBlock;
 /**
- * Retrieves all objects matching the given cmis query.
+ * Retrieves all objects matching the given cmis query string.
  * completionBlock returns the search results as a paged results object or nil 
if unsuccessful.
  */
 - (CMISRequest*)query:(NSString *)statement 
searchAllVersions:(BOOL)searchAllVersion
                                       completionBlock:(void 
(^)(CMISPagedResult *pagedResult, NSError *error))completionBlock;
 
 /**
- * Retrieves all objects matching the given cmis query, as CMISQueryResult 
objects.
+ * Retrieves all objects matching the given cmis query string, as 
CMISQueryResult objects.
  * and using the parameters provided in the operation context.
  * completionBlock returns the search results as a paged results object or nil 
if unsuccessful.
  */
@@ -130,6 +131,22 @@
                                       completionBlock:(void 
(^)(CMISPagedResult *pagedResult, NSError *error))completionBlock;
 
 /**
+ * Retrieves all objects matching the given cmis query statement.
+ * completionBlock returns the search results as a paged results object or nil 
if unsuccessful.
+ */
+- (CMISRequest*)queryStatement:(CMISQueryStatement *)queryStatement 
searchAllVersions:(BOOL)searchAllVersion
+      completionBlock:(void (^)(CMISPagedResult *pagedResult, NSError 
*error))completionBlock;
+
+/**
+ * Retrieves all objects matching the given cmis query statement, as 
CMISQueryResult objects.
+ * and using the parameters provided in the operation context.
+ * completionBlock returns the search results as a paged results object or nil 
if unsuccessful.
+ */
+- (CMISRequest*)queryStatement:(CMISQueryStatement *)queryStatement 
searchAllVersions:(BOOL)searchAllVersion
+     operationContext:(CMISOperationContext *)operationContext
+      completionBlock:(void (^)(CMISPagedResult *pagedResult, NSError 
*error))completionBlock;
+
+/**
  * Queries for a specific type of objects.
  * Returns a paged result set, containing CMISObject instances.
  * completionBlock returns the search results as a paged results object or nil 
if unsuccessful.
@@ -140,6 +157,18 @@
               operationContext:(CMISOperationContext *)operationContext
                completionBlock:(void (^)(CMISPagedResult *result, NSError 
*error))completionBlock;
 
+/**
+ * Queries for a specific type of objects.
+ * Pass where clause as query statement to ensure correct escaping
+ * Returns a paged result set, containing CMISObject instances.
+ * completionBlock returns the search results as a paged results object or nil 
if unsuccessful.
+ */
+- (CMISRequest*)queryObjectsWithTypeid:(NSString *)typeId
+                        whereStatement:(CMISQueryStatement *)whereStatement
+                     searchAllVersions:(BOOL)searchAllVersion
+                      operationContext:(CMISOperationContext *)operationContext
+                       completionBlock:(void (^)(CMISPagedResult *result, 
NSError *error))completionBlock;
+
 
 /**
  * Creates a folder in the provided folder.

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m
URL: 
http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m?rev=1611348&r1=1611347&r2=1611348&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m Thu Jul 17 
12:55:59 2014
@@ -274,9 +274,28 @@
     }];
 }
 
+- (CMISRequest*)queryStatement:(CMISQueryStatement *)queryStatement 
searchAllVersions:(BOOL)searchAllVersion
+      completionBlock:(void (^)(CMISPagedResult *pagedResult, NSError 
*error))completionBlock {
+        return [self query:[queryStatement queryString]
+        searchAllVersions:searchAllVersion
+          completionBlock:completionBlock];
+}
+
+- (CMISRequest*)queryStatement:(CMISQueryStatement *)queryStatement 
searchAllVersions:(BOOL)searchAllVersion
+     operationContext:(CMISOperationContext *)operationContext
+      completionBlock:(void (^)(CMISPagedResult *pagedResult, NSError 
*error))completionBlock {
+        return [self query:[queryStatement queryString]
+         searchAllVersions:searchAllVersion
+          operationContext:operationContext
+           completionBlock:completionBlock];
+}
+
 - (CMISRequest*)query:(NSString *)statement 
searchAllVersions:(BOOL)searchAllVersion completionBlock:(void 
(^)(CMISPagedResult *pagedResult, NSError *error))completionBlock
 {
-    return [self query:statement searchAllVersions:searchAllVersion 
operationContext:[CMISOperationContext defaultOperationContext] 
completionBlock:completionBlock];
+    return [self query:statement
+     searchAllVersions:searchAllVersion
+      operationContext:[CMISOperationContext defaultOperationContext]
+       completionBlock:completionBlock];
 }
 
 - (CMISRequest*)query:(NSString *)statement 
searchAllVersions:(BOOL)searchAllVersion
@@ -403,10 +422,31 @@
 }
 
 - (CMISRequest*)queryObjectsWithTypeid:(NSString *)typeId
-                   whereClause:(NSString *)whereClause
-             searchAllVersions:(BOOL)searchAllVersion
-              operationContext:(CMISOperationContext *)operationContext
-               completionBlock:(void (^)(CMISPagedResult *result, NSError 
*error))completionBlock
+                           whereClause:(NSString *)whereClause
+                     searchAllVersions:(BOOL)searchAllVersion
+                      operationContext:(CMISOperationContext *)operationContext
+                       completionBlock:(void (^)(CMISPagedResult *result, 
NSError *error))completionBlock
+{
+    return [self retrieveTypeDefinition:typeId
+                        completionBlock:^(CMISTypeDefinition *typeDefinition, 
NSError *internalError) {
+                            if (internalError != nil) {
+                                NSError *error = [CMISErrors 
cmisError:internalError cmisErrorCode:kCMISErrorCodeRuntime];
+                                completionBlock(nil, error);
+                            } else {
+                                [self 
queryObjectsWithTypeDefinition:typeDefinition
+                                                         
whereClause:whereClause
+                                                   
searchAllVersions:searchAllVersion
+                                                    
operationContext:operationContext
+                                                     
completionBlock:completionBlock];
+                            }
+                        }];
+}
+
+- (CMISRequest*)queryObjectsWithTypeid:(NSString *)typeId
+                        whereStatement:(CMISQueryStatement *)whereStatement
+                     searchAllVersions:(BOOL)searchAllVersion
+                      operationContext:(CMISOperationContext *)operationContext
+                       completionBlock:(void (^)(CMISPagedResult *result, 
NSError *error))completionBlock
 {
     return [self retrieveTypeDefinition:typeId
                  completionBlock:^(CMISTypeDefinition *typeDefinition, NSError 
*internalError) {
@@ -414,11 +454,11 @@
                          NSError *error = [CMISErrors cmisError:internalError 
cmisErrorCode:kCMISErrorCodeRuntime];
                          completionBlock(nil, error);
                      } else {
-                         [self queryObjectsWithTypeDefinition:typeDefinition
-                                                  whereClause:whereClause
-                                            searchAllVersions:searchAllVersion
-                                             operationContext:operationContext
-                                              completionBlock:completionBlock];
+                         [self queryObjectsWithTypeid:typeId
+                                          whereClause:[whereStatement 
queryString]
+                                    searchAllVersions:searchAllVersion
+                                     operationContext:operationContext
+                                      completionBlock:completionBlock];
                      }
                  }];
 }

Modified: chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m
URL: 
http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m?rev=1611348&r1=1611347&r2=1611348&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m 
(original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m Thu 
Jul 17 12:55:59 2014
@@ -43,6 +43,7 @@
 #import "CMISLog.h"
 #import "CMISURLUtil.h"
 #import "CMISMimeHelper.h"
+#import "CMISQueryStatement.h"
 
 @interface ObjectiveCMISTests ()
 
@@ -2291,4 +2292,110 @@
     XCTAssertEqualObjects(@"%C3%BC%C3%A4%C3%B6%C3%9C%C3%84%C3%96%C3%A9%C4%9F", 
[CMISURLUtil encodeUrlParameterValue:@"üäöÜÄÖéğ"], @"wrong encoded url 
parameter value");
 }
 
+- (void)testQueryStatementStaticQueries {
+    NSString *query;
+    CMISQueryStatement *st;
+    
+    query = @"SELECT cmis:name FROM cmis:folder";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    XCTAssertEqualObjects(query, [st queryString], @"wrong encoded query 
statement");
+    
+    query = @"SELECT * FROM cmis:document WHERE cmis:createdBy = \'admin\' AND 
abc:int = 42";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    XCTAssertEqualObjects(query, [st queryString], @"wrong encoded query 
statement");
+    
+    query = @"SELECT * FROM cmis:document WHERE abc:test = 'x?z'";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setStringAtIndex:1 string:@"y"];
+    XCTAssertEqualObjects(query, [st queryString], @"wrong encoded query 
statement");
+}
+
+- (void)testQueryStatementWherePlacholder {
+    NSString *query;
+    CMISQueryStatement *st;
+    
+    // strings
+    query = @"SELECT * FROM cmis:document WHERE abc:string = ?";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setStringAtIndex:1 string:@"test"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:string = 
'test'", [st queryString], @"wrong encoded query statement");
+    
+    query = @"SELECT * FROM cmis:document WHERE abc:string = ?";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setStringAtIndex:1 string:@"te'st"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:string = 
'te\\'st'", [st queryString], @"wrong encoded query statement");
+    
+    // likes
+    query = @"SELECT * FROM cmis:document WHERE abc:string LIKE ?";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setStringLikeAtIndex:1 string:@"%test%"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:string LIKE 
'%test%'", [st queryString], @"wrong encoded query statement");
+    
+    query = @"SELECT * FROM cmis:document WHERE abc:string LIKE ?";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setStringLikeAtIndex:1 string:@"\\_test\\%blah\\\\blah"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:string LIKE 
'\\_test\\%blah\\\\\\\\blah'", [st queryString], @"wrong encoded query 
statement");
+    
+    // contains
+    
+    // *, ? and - are treated as text search operators: 1st level escaping:
+    // none, 2nd level escaping: none
+    // \*, \? and \- are used as literals, 1st level escaping: none, 2nd
+    // level escaping: \\*, \\?, \\-
+    // ' and " are used as literals, 1st level escaping: \', \", 2nd level
+    // escaping: \\\', \\\",
+    // \ plus any other character, 1st level escaping \\ plus character, 2nd
+    // level: \\\\ plus character
+    
+    query = @"SELECT * FROM cmis:document WHERE CONTAINS(?)";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setStringContainsAtIndex:1 string:@"John's"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE 
CONTAINS('John\\\\\\'s')", [st queryString], @"wrong encoded query statement");
+    [st setStringContainsAtIndex:1 string:@"foo -bar"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE CONTAINS('foo 
-bar')", [st queryString], @"wrong encoded query statement");
+    [st setStringContainsAtIndex:1 string:@"foo*"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE 
CONTAINS('foo*')", [st queryString], @"wrong encoded query statement");
+    [st setStringContainsAtIndex:1 string:@"foo?"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE 
CONTAINS('foo?')", [st queryString], @"wrong encoded query statement");
+    [st setStringContainsAtIndex:1 string:@"foo\\-bar"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE 
CONTAINS('foo\\\\-bar')", [st queryString], @"wrong encoded query statement");
+    [st setStringContainsAtIndex:1 string:@"foo\\*"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE 
CONTAINS('foo\\\\*')", [st queryString], @"wrong encoded query statement");
+    [st setStringContainsAtIndex:1 string:@"foo\\?"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE 
CONTAINS('foo\\\\?')", [st queryString], @"wrong encoded query statement");
+    [st setStringContainsAtIndex:1 string:@"\"Cool\""];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE 
CONTAINS('\\\\\\\"Cool\\\\\\\"')", [st queryString], @"wrong encoded query 
statement");
+    [st setStringContainsAtIndex:1 string:@"c:\\MyDcuments"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE 
CONTAINS('c:\\\\MyDcuments')", [st queryString], @"wrong encoded query 
statement");
+    
+    // ids
+    query = @"SELECT * FROM cmis:document WHERE abc:id = ?";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setStringAtIndex:1 string:@"123"];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:id = '123'", 
[st queryString], @"wrong encoded query statement");
+    
+    // booleans
+    query = @"SELECT * FROM cmis:document WHERE abc:bool = ?";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setBooleanAtIndex:1 boolean:YES];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:bool = YES", 
[st queryString], @"wrong encoded query statement");
+    
+    // numbers
+    query = @"SELECT * FROM cmis:document WHERE abc:int = ? AND abc:int2 = 
123";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    [st setNumberAtIndex:1 number:[NSNumber numberWithInt:42]];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:int = 42 AND 
abc:int2 = 123", [st queryString], @"wrong encoded query statement");
+    
+    query = @"SELECT * FROM cmis:document WHERE abc:dateTime = ?";
+    st = [[CMISQueryStatement alloc] initWithStatement:query];
+    NSDateFormatter *df = [NSDateFormatter new];
+    [df setDateFormat:@"dd/MM/yyyy HH:mm:ss"];
+    //Create the GMT date
+    df.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
+    NSDate *date = [df dateFromString:@"02/02/2012 03:04:05"];
+
+    [st setDateTimeAtIndex:1 date:date];
+    XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:dateTime = 
TIMESTAMP '2012-02-02T03:04:05.000Z'", [st queryString], @"wrong encoded query 
statement");
+}
+
 @end


Reply via email to