Skip to content

Commit

Permalink
- Added the source for PONSO support classes
Browse files Browse the repository at this point in the history
  • Loading branch information
nikita-zhuk committed Jun 4, 2011
1 parent 7104d99 commit 7d458b1
Show file tree
Hide file tree
Showing 7 changed files with 634 additions and 0 deletions.
47 changes: 47 additions & 0 deletions ponso/MKCDAGNode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Copyright 2011 Marko Karppinen & Co. LLC.
Licensed 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.
MKCDAGNode.h
Created by Nikita Zhuk on 22.1.2011.
*/

#import <Foundation/Foundation.h>

/**
Generic DAG (Directed Acyclic Graph) implementation
*/
@interface MKCDAGNode : NSObject
{
id object;
NSMutableArray *nodes;
}

//! Generic payload object, not used in the algorithm. Can be nil.
@property(nonatomic, retain) id object;

//! All objects of nodes in topological order which are reachable from the receiver.
@property(nonatomic, readonly) NSArray *objectsInTopologicalOrder;

- (id)initWithObject:(id)object;

/**
Creates dependency between receiver and the given node and adds the given node into the DAG.
The dependency direction is from the receiver to the given node, e.g. [a addNode:b] means that 'a' depends on 'b'.
If the new node would create a cycle in the DAG it's not added and NO is returned.
YES is returned otherwise.
*/
- (BOOL)addNode:(MKCDAGNode *)node;

@end
132 changes: 132 additions & 0 deletions ponso/MKCDAGNode.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
Copyright 2011 Marko Karppinen & Co. LLC.
Licensed 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.
MKCDAGNode.m
Created by Nikita Zhuk on 22.1.2011.
*/

#import "MKCDAGNode.h"

@interface MKCDAGNode()
/**
All nodes directly reachable from this node ( = nodes to which there is a directed edge from 'self' node)
*/
@property(nonatomic, retain) NSMutableArray *nodes;
@end

@implementation MKCDAGNode

#pragma mark Algorithms

+ (BOOL)isCyclicNode:(MKCDAGNode *)node visitedNodes:(NSMutableSet *)visitedNodes
{
if([visitedNodes intersectsSet:[NSSet setWithArray:node.nodes]])
{
// We've seen these nodes already - a cycle!
return YES;
}

for (MKCDAGNode *childNode in node.nodes)
{
[visitedNodes addObject:childNode];
BOOL childNodeIsCyclic = [self isCyclicNode:childNode visitedNodes:visitedNodes];
[visitedNodes removeObject:childNode];

if(childNodeIsCyclic)
{
return YES;
}
}

return NO;
}

// DAG topological order visitor, see http://en.wikipedia.org/wiki/Directed_acyclic_graph
// 'visitedNodes' set is used to 'mark' visited nodes.
+ (void)visitNode:(MKCDAGNode *)node visitedNodes:(NSMutableSet *)visitedNodes orderedNodes:(NSMutableArray *)orderedNodes
{
if([visitedNodes containsObject:node])
{
return;
}

[visitedNodes addObject:node];

for (MKCDAGNode *childNode in node.nodes)
{
[self visitNode:childNode visitedNodes:visitedNodes orderedNodes:orderedNodes];
}

[orderedNodes addObject:node];
}

#pragma mark Public

- (id)initWithObject:(id)anObject
{
if((self = [super init]))
{
self.nodes = [NSMutableArray array];
self.object = anObject;
}
return self;
}

- (NSArray *)objectsInTopologicalOrder
{
NSMutableArray *orderedNodes = [NSMutableArray array];

[[self class] visitNode:self visitedNodes:[NSMutableSet set] orderedNodes:orderedNodes];

NSMutableArray *orderedObjects = [NSMutableArray array];
for (MKCDAGNode *node in orderedNodes)
{
if(node.object != nil)
{
[orderedObjects addObject:node.object];
}
}
return orderedObjects;
}

- (BOOL)addNode:(MKCDAGNode *)node
{
if(node == nil)
return NO;

if(![self.nodes containsObject:node])
[self.nodes addObject:node];

// Check that this node didn't cause a cycle - if it did, remove it.
BOOL isCyclic = [[self class] isCyclicNode:node visitedNodes:[NSMutableSet set]];
if(isCyclic)
{
[self.nodes removeObject:node];
}

return !isCyclic;
}

- (void) dealloc
{
self.nodes = nil;
self.object = nil;

[super dealloc];
}

@synthesize nodes;
@synthesize object;
@end
28 changes: 28 additions & 0 deletions ponso/MKCNSEntityDescriptionAdditions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copyright 2011 Marko Karppinen & Co. LLC.
Licensed 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.
MKCNSEntityDescriptionAdditions.h
Created by Nikita Zhuk on 22.1.2011.
*/

#import <CoreData/CoreData.h>


@interface NSEntityDescription(MKCNSEntityDescriptionAdditions)

/** @TypeInfo NSAttributeDescription */
@property(nonatomic, readonly) NSArray *noninheritedRelationshipsInIDKeyPathTopologicalOrder;

@end
83 changes: 83 additions & 0 deletions ponso/MKCNSEntityDescriptionAdditions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Copyright 2011 Marko Karppinen & Co. LLC.
Licensed 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.
MKCNSEntityDescriptionAdditions.m
Created by Nikita Zhuk on 22.1.2011.
*/


#import "MKCNSEntityDescriptionAdditions.h"
#import "MKCNSManagedObjectModelAdditions.h"

@interface MKCNSRelationshipDescriptionIDKeyPathDependencyFilter : NSObject <MKCNSRelationshipDescriptionDependencyFilter> @end

@implementation MKCNSRelationshipDescriptionIDKeyPathDependencyFilter

- (BOOL)includeRelationship:(NSRelationshipDescription *)relationship
{
if([[relationship entity] isEqual:[relationship destinationEntity]])
{
// Relationship from entity to itself - ignore.
return NO;
}

return [[[relationship userInfo] objectForKey:@"destinationEntityIDKeyPath"] length] > 0;
}

@end

@implementation NSEntityDescription(MKCNSEntityDescriptionAdditions)

/** @TypeInfo NSAttributeDescription */
- (NSArray*)noninheritedRelationshipsInIDKeyPathTopologicalOrder
{
NSArray *relationships = nil;

NSEntityDescription *superentity = [self superentity];
if (superentity != nil)
{
NSMutableArray *result = [[[[self relationshipsByName] allValues] mutableCopy] autorelease];
[result removeObjectsInArray:[[superentity relationshipsByName] allValues]];
relationships = result;
}
else
{
relationships = [[self relationshipsByName] allValues];
}

// Initially, sort relationships in alphabetical order.
relationships = [relationships sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]];

// Sort relationships in topological order by their destination entities including "IDKeyPath" relationships in dependencies.
// Although this is a bit naive O(n^2) sort, it should be fast enough since n (=number of relationships) is usually very low.
id IDKeyPathDependencyFilter = [[[MKCNSRelationshipDescriptionIDKeyPathDependencyFilter alloc] init] autorelease];
NSArray *allEntities = [[self managedObjectModel] entitiesInTopologicalOrderUsingDependencyFilter:IDKeyPathDependencyFilter];
NSMutableArray *sortedRelationships = [NSMutableArray arrayWithCapacity:[relationships count]];

for (NSEntityDescription *entity in allEntities)
{
for (NSRelationshipDescription *relationship in relationships)
{
if([[relationship destinationEntity] isEqual:entity])
{
[sortedRelationships addObject:relationship];
}
}
}

return sortedRelationships;
}

@end
44 changes: 44 additions & 0 deletions ponso/MKCNSManagedObjectModelAdditions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Copyright 2011 Marko Karppinen & Co. LLC.
Licensed 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.
MKCNSManagedObjectModelAdditions.h
Created by Nikita Zhuk on 22.1.2011.
*/

#import <CoreData/CoreData.h>


@protocol MKCNSRelationshipDescriptionDependencyFilter<NSObject>
- (BOOL)includeRelationship:(NSRelationshipDescription *)relationship;
@end

@interface NSManagedObjectModel(MKCNSManagedObjectModelAdditions)

/**
Array of NSEntityDescription objects, sorted in topological order
based on relationships between entity descriptions.
If there are cyclic dependencies, a nil is returned.
This method counts all relationships as dependencies if they are not marked as being 'transient'.
*/

- (NSArray *) entitiesInTopologicalOrder;

/*
Same as entitiesInTopologicalOrder, but uses the given dependencyFilter to decide whether a
relationship should be counted as dependency or not.
*/
- (NSArray *) entitiesInTopologicalOrderUsingDependencyFilter:(id<MKCNSRelationshipDescriptionDependencyFilter>) dependencyFilter;

@end
Loading

0 comments on commit 7d458b1

Please sign in to comment.