Permalink
Browse files

Descendant combinator

  • Loading branch information...
1 parent f17ad47 commit 199261d0a8dc957cf47202852f5a9514964855a6 @dhemery committed Apr 14, 2012
@@ -93,9 +93,12 @@
E621659DB794A5D612DCD49E /* FakeSimpleParser.m in Sources */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD49D /* FakeSimpleParser.m */; };
E621659DB794A5D612DCD4A0 /* FakeSimpleParser.h in Headers */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD49F /* FakeSimpleParser.h */; };
E621659DB794A5D612DCD4A2 /* IdentityCombinatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD4A1 /* IdentityCombinatorTests.m */; };
- E621659DB794A5D612DCD4A6 /* Combinator.h in Headers */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD4A5 /* Combinator.h */; };
E621659DB794A5D612DCD4A8 /* IdentityCombinator.m in Sources */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD4A7 /* IdentityCombinator.m */; };
E621659DB794A5D612DCD4AA /* IdentityCombinator.h in Headers */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD4A9 /* IdentityCombinator.h */; };
+ E621659DB794A5D612DCD4AC /* DescendantCombinatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD4AB /* DescendantCombinatorTests.m */; };
+ E621659DB794A5D612DCD4B0 /* DescendantCombinator.m in Sources */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD4AF /* DescendantCombinator.m */; };
+ E621659DB794A5D612DCD4B2 /* DescendantCombinator.h in Headers */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD4B1 /* DescendantCombinator.h */; };
+ E621659DB794A5D612DCD4B4 /* Combinator.h in Headers */ = {isa = PBXBuildFile; fileRef = E621659DB794A5D612DCD4B3 /* Combinator.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -217,9 +220,12 @@
E621659DB794A5D612DCD49D /* FakeSimpleParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FakeSimpleParser.m; sourceTree = "<group>"; };
E621659DB794A5D612DCD49F /* FakeSimpleParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FakeSimpleParser.h; sourceTree = "<group>"; };
E621659DB794A5D612DCD4A1 /* IdentityCombinatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IdentityCombinatorTests.m; sourceTree = "<group>"; };
- E621659DB794A5D612DCD4A5 /* Combinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Combinator.h; sourceTree = "<group>"; };
E621659DB794A5D612DCD4A7 /* IdentityCombinator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IdentityCombinator.m; sourceTree = "<group>"; };
E621659DB794A5D612DCD4A9 /* IdentityCombinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IdentityCombinator.h; sourceTree = "<group>"; };
+ E621659DB794A5D612DCD4AB /* DescendantCombinatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DescendantCombinatorTests.m; sourceTree = "<group>"; };
+ E621659DB794A5D612DCD4AF /* DescendantCombinator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DescendantCombinator.m; sourceTree = "<group>"; };
+ E621659DB794A5D612DCD4B1 /* DescendantCombinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DescendantCombinator.h; sourceTree = "<group>"; };
+ E621659DB794A5D612DCD4B3 /* Combinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Combinator.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -326,9 +332,11 @@
2D695CE1147CD26700AF4115 /* matchers */ = {
isa = PBXGroup;
children = (
+ E621659DB794A5D612DCD4B3 /* Combinator.h */,
+ E621659DB794A5D612DCD4B1 /* DescendantCombinator.h */,
+ E621659DB794A5D612DCD4AF /* DescendantCombinator.m */,
E621659DB794A5D612DCD4A9 /* IdentityCombinator.h */,
E621659DB794A5D612DCD4A7 /* IdentityCombinator.m */,
- E621659DB794A5D612DCD4A5 /* Combinator.h */,
E621659DB794A5D612DCD478 /* ComplexMatcher.m */,
E621659DB794A5D612DCD47A /* ComplexMatcher.h */,
E621659DB794A5D612DCD45E /* UniversalMatcher.m */,
@@ -411,6 +419,7 @@
2D695CE7147CD38000AF4115 /* matchers */ = {
isa = PBXGroup;
children = (
+ E621659DB794A5D612DCD4AB /* DescendantCombinatorTests.m */,
E621659DB794A5D612DCD4A1 /* IdentityCombinatorTests.m */,
E621659DB794A5D612DCD474 /* ComplexMatcherTests.m */,
2D695CD0147CD1D200AF4115 /* KindOfClassMatcherTests.m */,
@@ -554,8 +563,9 @@
E621659DB794A5D612DCD48B /* SubjectPatternParser.h in Headers */,
E621659DB794A5D612DCD48F /* IgorQueryParser.h in Headers */,
E621659DB794A5D612DCD49C /* BranchParser.h in Headers */,
- E621659DB794A5D612DCD4A6 /* Combinator.h in Headers */,
E621659DB794A5D612DCD4AA /* IdentityCombinator.h in Headers */,
+ E621659DB794A5D612DCD4B2 /* DescendantCombinator.h in Headers */,
+ E621659DB794A5D612DCD4B4 /* Combinator.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -700,6 +710,7 @@
E621659DB794A5D612DCD495 /* InstanceParserTests.m in Sources */,
E621659DB794A5D612DCD49E /* FakeSimpleParser.m in Sources */,
E621659DB794A5D612DCD4A2 /* IdentityCombinatorTests.m in Sources */,
+ E621659DB794A5D612DCD4AC /* DescendantCombinatorTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -736,6 +747,7 @@
E621659DB794A5D612DCD479 /* ComplexMatcher.m in Sources */,
E621659DB794A5D612DCD49A /* BranchParser.m in Sources */,
E621659DB794A5D612DCD4A8 /* IdentityCombinator.m in Sources */,
+ E621659DB794A5D612DCD4B0 /* DescendantCombinator.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
@@ -1,5 +1,4 @@
-@protocol Combinator <NSObject>
+#import "SubjectMatcher.h"
-- (BOOL)collectMatchingRelativesOfViews:(NSArray *)targetViews inTree:(UIView *)tree intoArray:(NSMutableArray *)matchingRelatives;
-
-@end
+@protocol Combinator <SubjectMatcher>
+@end
@@ -0,0 +1,9 @@
+#import "Combinator.h"
+
+@protocol SimpleMatcher;
+
+@interface DescendantCombinator : NSObject <Combinator>
+
++ (id <Combinator>)combinatorWithSubjectMatcher:(id <SimpleMatcher>)theSubjectMatcher relativeMatcher:(id <SubjectMatcher>)theRelativeMatcher;
+
+@end
@@ -0,0 +1,50 @@
+#import "DescendantCombinator.h"
+#import "SimpleMatcher.h"
+
+@implementation DescendantCombinator {
+ id <SimpleMatcher> subjectMatcher;
+ id <SubjectMatcher> relativeMatcher;
+}
+
+- (NSMutableArray *)ancestorsOfView:(UIView *)view {
+ NSMutableArray *ancestors = [NSMutableArray array];
+ id ancestor = [view superview];
+ while (ancestor) {
+ [ancestors addObject:ancestor];
+ ancestor = [ancestor superview];
+ }
+ return ancestors;
+}
+
+- (NSMutableArray *)ancestorsOfView:(UIView *)view inTree:(UIView *)root {
+ NSMutableArray *ancestorsOfView = [self ancestorsOfView:view];
+ [ancestorsOfView removeObjectsInArray:[self ancestorsOfView:root]];
+ return ancestorsOfView;
+}
+
++ (id <Combinator>)combinatorWithSubjectMatcher:(id <SimpleMatcher>)subjectMatcher relativeMatcher:(id <SubjectMatcher>)relativeMatcher {
+ return [[self alloc] initWithSubjectMatcher:subjectMatcher relativeMatcher:relativeMatcher];
+}
+
+- (id <Combinator>)initWithSubjectMatcher:(id <SimpleMatcher>)theSubjectMatcher relativeMatcher:(id <SubjectMatcher>)theRelativeMatcher {
+ self = [super init];
+ if (self) {
+ subjectMatcher = theSubjectMatcher;
+ relativeMatcher = theRelativeMatcher;
+ }
+ return self;
+}
+
+- (BOOL)matchesView:(UIView *)subject inTree:(UIView *)tree {
+ if (![subjectMatcher matchesView:subject]) {
+ return NO;
+ }
+ for(id ancestor in [self ancestorsOfView:subject inTree:tree]) {
+ if([relativeMatcher matchesView:ancestor inTree:tree]) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+@end
@@ -1,10 +1,9 @@
#import "Combinator.h"
-@protocol SubjectMatcher;
+@protocol SimpleMatcher;
@interface IdentityCombinator : NSObject <Combinator>
-- (id <Combinator>)initWithSubjectMatcher:(id <SubjectMatcher>)aSubjectMatcher;
++ (id <Combinator>)combinatorWithSubjectMatcher:(id <SimpleMatcher>)subjectMatcher;
-+ (id <Combinator>)combinatorThatAppliesMatcher:(id <SubjectMatcher>)subjectMatcher;
-@end
+@end
View
@@ -1,29 +1,22 @@
#import "IdentityCombinator.h"
-#import "SubjectMatcher.h"
+#import "SimpleMatcher.h"
@implementation IdentityCombinator {
- id <SubjectMatcher> subjectMatcher;
+ id <SimpleMatcher> subjectMatcher;
}
-- (id <Combinator>)initWithSubjectMatcher:(id <SubjectMatcher>)theSubjectMatcher {
+- (id <Combinator>)initWithSubjectMatcher:(id <SimpleMatcher>)theSubjectMatcher {
self = [super init];
if (self) {
subjectMatcher = theSubjectMatcher;
}
return self;
}
-- (BOOL)collectMatchingRelativesOfViews:(NSArray *)targetViews inTree:(UIView *)tree intoArray:(NSMutableArray *)matchingRelatives {
- BOOL foundMatchingViews = NO;
- for(id view in targetViews) {
- if([subjectMatcher matchesView:view inTree:tree]) {
- foundMatchingViews = YES;
- [matchingRelatives addObject:view];
- }
- }
- return foundMatchingViews;
+- (BOOL)matchesView:(UIView *)view inTree:(UIView *)tree {
+ return [subjectMatcher matchesView:view];
}
-+ (id <Combinator>)combinatorThatAppliesMatcher:(id <SubjectMatcher>)subjectMatcher {
++ (id <Combinator>)combinatorWithSubjectMatcher:(id <SimpleMatcher>)subjectMatcher {
return [[self alloc] initWithSubjectMatcher:subjectMatcher];
}
-@end
+@end
@@ -0,0 +1,77 @@
+#import "ViewFactory.h"
+#import "Combinator.h"
+#import "UniversalMatcher.h"
+#import "DescendantCombinator.h"
+#import "MatchesView.h"
+#import "FalseMatcher.h"
+#import "IdentityMatcher.h"
+
+@interface DescendantCombinatorTests : SenTestCase
+@end
+
+@implementation DescendantCombinatorTests {
+ UIView *subject;
+ UIView *parent;
+ UIView *grandParent;
+ UIView *greatGrandParent;
+ UIView *greatGreatGrandParent;
+ UIView *greatGreatGreatGrandParent;
+}
+
+- (void)setUp {
+ subject = [ViewFactory view];
+ parent = [ViewFactory view];
+ grandParent = [ViewFactory view];
+ greatGrandParent = [ViewFactory view];
+ greatGreatGrandParent = [ViewFactory view];
+ greatGreatGreatGrandParent = [ViewFactory view];
+ [parent addSubview:subject];
+ [grandParent addSubview:parent];
+ [greatGrandParent addSubview:grandParent];
+ [greatGreatGrandParent addSubview:greatGrandParent];
+ [greatGreatGreatGrandParent addSubview:greatGreatGrandParent];
+}
+
+- (void)testMatchesIfViewMatchesSubjectMatcherAndAncestorMatchesRelativeMatcher {
+ id <SubjectMatcher> grandParentMatcher = [IdentityMatcher forView:grandParent];
+ id <Combinator> combinator = [DescendantCombinator combinatorWithSubjectMatcher:[UniversalMatcher new]
+ relativeMatcher:grandParentMatcher];
+
+ assertThat(combinator, [MatchesView view:subject inTree:greatGreatGreatGrandParent]);
+}
+
+- (void)testSearchesToRootForMatchingAncestor {
+ IdentityMatcher *greatGreatGreatGrandParentMatcher = [IdentityMatcher forView:greatGreatGreatGrandParent];
+ id <Combinator> combinator = [DescendantCombinator combinatorWithSubjectMatcher:[UniversalMatcher new]
+ relativeMatcher:greatGreatGreatGrandParentMatcher];
+
+ assertThat(combinator, [MatchesView view:subject inTree:greatGreatGreatGrandParent]);
+}
+
+- (void)testMismatchesIfViewMismatchesSubjectMatcher {
+
+ id <Combinator> combinator = [DescendantCombinator combinatorWithSubjectMatcher:[FalseMatcher new]
+ relativeMatcher:[UniversalMatcher new]];
+
+ assertThat(combinator, isNot([MatchesView view:subject inTree:greatGreatGreatGrandParent]));
+}
+
+- (void)testMismatchesIfViewMatchesSubjectMatcherAndNoAncestorMatchesRelativeMatcher {
+
+ id <Combinator> combinator = [DescendantCombinator combinatorWithSubjectMatcher:[UniversalMatcher new]
+ relativeMatcher:[FalseMatcher new]];
+
+ assertThat(combinator, isNot([MatchesView view:subject inTree:greatGreatGreatGrandParent]));
+}
+
+- (void)testMismatchesIfMatchingAncestorIsAboveTree {
+
+ id <Combinator> combinator = [DescendantCombinator combinatorWithSubjectMatcher:[UniversalMatcher new]
+ relativeMatcher:[IdentityMatcher forView:greatGreatGreatGrandParent]];
+
+ assertThat(combinator, isNot([MatchesView view:subject inTree:grandParent]));
+}
+
+@end
+
+
View
@@ -1,4 +1,5 @@
#import "SubjectMatcher.h"
+#import "SimpleMatcher.h"
-@interface FalseMatcher : NSObject <SubjectMatcher>
+@interface FalseMatcher : NSObject <SubjectMatcher, SimpleMatcher>
@end
View
@@ -2,6 +2,10 @@
@implementation FalseMatcher
+- (BOOL)matchesView:(UIView *)view {
+ return NO;
+}
+
- (BOOL)matchesView:(UIView *)view inTree:(UIView *)root {
return NO;
}
@@ -1,64 +1,26 @@
#import "ViewFactory.h"
#import "SubjectMatcher.h"
#import "IdentityMatcher.h"
-#import "Combinator.h"
#import "IdentityCombinator.h"
#import "FalseMatcher.h"
#import "UniversalMatcher.h"
+#import "MatchesView.h"
@interface IdentityCombinatorTests : SenTestCase
@end
-@implementation IdentityCombinatorTests {
- NSMutableArray *matchingRelatives;
- NSMutableArray *targetViews;
-}
-
-- (void)setUp {
- matchingRelatives = [NSMutableArray array];
- targetViews = [NSMutableArray array];
-}
-
-- (void)testDeliversViewIfViewMatchesInstanceMatcher {
- UIView *view1 = [ViewFactory view];
- UIView *view2 = [ViewFactory view];
- [targetViews addObject:view1];
- [targetViews addObject:view2];
- id <Combinator> combinator = [IdentityCombinator combinatorThatAppliesMatcher:[UniversalMatcher new]];
+@implementation IdentityCombinatorTests
- [combinator collectMatchingRelativesOfViews:targetViews inTree:nil intoArray:matchingRelatives];
+- (void)testMatchesViewIfSubjectMatcherMatches {
+ id <Combinator> combinator = [IdentityCombinator combinatorWithSubjectMatcher:[UniversalMatcher new]];
- assertThat(matchingRelatives, contains(sameInstance(view1), sameInstance(view2), nil));
+ assertThat(combinator, [MatchesView view:[ViewFactory view] inTree:nil]);
}
-- (void)testReportsViewsDeliveredIfViewsDelivered {
- UIView *view1 = [ViewFactory view];
- [targetViews addObject:view1];
- id <Combinator> combinator = [IdentityCombinator combinatorThatAppliesMatcher:[UniversalMatcher new]];
-
- BOOL delivered = [combinator collectMatchingRelativesOfViews:targetViews inTree:nil intoArray:matchingRelatives];
-
- assertThatBool(delivered, equalToBool(YES));
-}
-
-- (void)testDeliversNoViewIfViewMismatchesInstanceMatcher {
- UIView *view = [ViewFactory view];
- [targetViews addObject:view];
- id <Combinator> combinator = [IdentityCombinator combinatorThatAppliesMatcher:[FalseMatcher new]];
-
- [combinator collectMatchingRelativesOfViews:targetViews inTree:nil intoArray:matchingRelatives];
-
- assertThat(matchingRelatives, is(empty()));
-}
-
-- (void)testReportsNoViewsDeliveredIfNoViewsDelivered {
- UIView *view1 = [ViewFactory view];
- [targetViews addObject:view1];
- id <Combinator> combinator = [IdentityCombinator combinatorThatAppliesMatcher:[FalseMatcher new]];
-
- BOOL delivered = [combinator collectMatchingRelativesOfViews:targetViews inTree:nil intoArray:matchingRelatives];
+- (void)testMismatchesViewIfSubjectMatcherMismatches {
+ id <Combinator> combinator = [IdentityCombinator combinatorWithSubjectMatcher:[FalseMatcher new]];
- assertThatBool(delivered, equalToBool(NO));
+ assertThat(combinator, isNot([MatchesView view:[ViewFactory view] inTree:nil]));
}
@end
View
@@ -20,7 +20,7 @@ - (IdentityMatcher *)initForView:(UIView *)view {
}
- (BOOL)matchesView:(UIView *)view {
- return [self matchesView:view inTree:nil];
+ return matchView == view;
}
- (BOOL)matchesView:(UIView *)view inTree:(UIView *)root {

0 comments on commit 199261d

Please sign in to comment.