From 7d458b170610380b1dd932580681d41db0e0dbc8 Mon Sep 17 00:00:00 2001 From: Nikita Zhuk Date: Sat, 4 Jun 2011 11:39:04 +0300 Subject: [PATCH] - Added the source for PONSO support classes --- ponso/MKCDAGNode.h | 47 ++++++ ponso/MKCDAGNode.m | 132 +++++++++++++++ ponso/MKCNSEntityDescriptionAdditions.h | 28 ++++ ponso/MKCNSEntityDescriptionAdditions.m | 83 ++++++++++ ponso/MKCNSManagedObjectModelAdditions.h | 44 +++++ ponso/MKCNSManagedObjectModelAdditions.m | 98 +++++++++++ ponso/PONSO-LICENSE.txt | 202 +++++++++++++++++++++++ 7 files changed, 634 insertions(+) create mode 100644 ponso/MKCDAGNode.h create mode 100644 ponso/MKCDAGNode.m create mode 100644 ponso/MKCNSEntityDescriptionAdditions.h create mode 100644 ponso/MKCNSEntityDescriptionAdditions.m create mode 100644 ponso/MKCNSManagedObjectModelAdditions.h create mode 100644 ponso/MKCNSManagedObjectModelAdditions.m create mode 100644 ponso/PONSO-LICENSE.txt diff --git a/ponso/MKCDAGNode.h b/ponso/MKCDAGNode.h new file mode 100644 index 00000000..bb16510e --- /dev/null +++ b/ponso/MKCDAGNode.h @@ -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 + +/** + 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 diff --git a/ponso/MKCDAGNode.m b/ponso/MKCDAGNode.m new file mode 100644 index 00000000..6c722748 --- /dev/null +++ b/ponso/MKCDAGNode.m @@ -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 diff --git a/ponso/MKCNSEntityDescriptionAdditions.h b/ponso/MKCNSEntityDescriptionAdditions.h new file mode 100644 index 00000000..fd1b53d5 --- /dev/null +++ b/ponso/MKCNSEntityDescriptionAdditions.h @@ -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 + + +@interface NSEntityDescription(MKCNSEntityDescriptionAdditions) + +/** @TypeInfo NSAttributeDescription */ +@property(nonatomic, readonly) NSArray *noninheritedRelationshipsInIDKeyPathTopologicalOrder; + +@end diff --git a/ponso/MKCNSEntityDescriptionAdditions.m b/ponso/MKCNSEntityDescriptionAdditions.m new file mode 100644 index 00000000..03f001c4 --- /dev/null +++ b/ponso/MKCNSEntityDescriptionAdditions.m @@ -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 @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 diff --git a/ponso/MKCNSManagedObjectModelAdditions.h b/ponso/MKCNSManagedObjectModelAdditions.h new file mode 100644 index 00000000..94db08e0 --- /dev/null +++ b/ponso/MKCNSManagedObjectModelAdditions.h @@ -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 + + +@protocol MKCNSRelationshipDescriptionDependencyFilter +- (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) dependencyFilter; + +@end diff --git a/ponso/MKCNSManagedObjectModelAdditions.m b/ponso/MKCNSManagedObjectModelAdditions.m new file mode 100644 index 00000000..175e8948 --- /dev/null +++ b/ponso/MKCNSManagedObjectModelAdditions.m @@ -0,0 +1,98 @@ +/* + 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.m + Created by Nikita Zhuk on 22.1.2011. + */ + +#import "MKCNSManagedObjectModelAdditions.h" +#import "MKCDAGNode.h" + +@interface MKCNSRelationshipDescriptionDefaultDependencyFilter : NSObject @end + +@implementation MKCNSRelationshipDescriptionDefaultDependencyFilter + +- (BOOL)includeRelationship:(NSRelationshipDescription *)relationship +{ + if([[relationship entity] isEqual:[relationship destinationEntity]]) + { + // Relationship from entity to itself - ignore. + return NO; + } + + return ![relationship isTransient]; +} + +@end + + +@implementation NSManagedObjectModel(MKCNSManagedObjectModelAdditions) + +- (NSArray *) entitiesInTopologicalOrderUsingDependencyFilter:(id) dependencyFilter +{ + // Wrap entitites in DAG nodes and place them all into dict for faster access + NSMutableDictionary *entityNodes = [NSMutableDictionary dictionaryWithCapacity:[[self entities] count]]; + for (NSEntityDescription *entity in [self entities]) + { + MKCDAGNode *node = [[[MKCDAGNode alloc] initWithObject:entity] autorelease]; + [entityNodes setObject:node forKey:[entity name]]; + } + + // Create DAG from entities based on their relationships + MKCDAGNode *root = [[[MKCDAGNode alloc] initWithObject:nil] autorelease]; + + for (NSEntityDescription *entity in [self entities]) + { + MKCDAGNode *node = [entityNodes objectForKey:[entity name]]; + + if(![root addNode:node]) + { + NSLog(@"Couldn't add node of entity '%@' to root.", [entity name]); + return nil; + } + + for (NSRelationshipDescription *relationship in [[entity relationshipsByName] allValues]) + { + BOOL shouldInclude = YES; + + if(dependencyFilter != nil) + { + shouldInclude = [dependencyFilter includeRelationship:relationship]; + } + + if(shouldInclude) + { + MKCDAGNode *childNode = [entityNodes objectForKey:[[relationship destinationEntity] name]]; + + if(![node addNode:childNode]) + { + NSLog(@"Couldn't add dependency '%@' -> '%@'. A cycle was detected.", [[relationship entity] name], [[relationship destinationEntity] name]); + return nil; + } + } + } + } + + return root.objectsInTopologicalOrder; +} + +- (NSArray *) entitiesInTopologicalOrder +{ + id defaultFilter = [[[MKCNSRelationshipDescriptionDefaultDependencyFilter alloc] init] autorelease]; + + return [self entitiesInTopologicalOrderUsingDependencyFilter:defaultFilter]; +} + +@end diff --git a/ponso/PONSO-LICENSE.txt b/ponso/PONSO-LICENSE.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/ponso/PONSO-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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.