You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by lg...@apache.org on 2014/07/17 14:56:00 UTC

svn commit: r1611348 - in /chemistry/objectivecmis/trunk: ObjectiveCMIS.xcodeproj/ ObjectiveCMIS/Bindings/ ObjectiveCMIS/Client/ ObjectiveCMISTests/

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