2017-05-15 12:27:07 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright 2017 Google
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-06-22 07:58:52 -07:00
|
|
|
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
2017-05-15 12:27:07 -07:00
|
|
|
|
2020-06-22 07:58:52 -07:00
|
|
|
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
|
2017-05-15 12:27:07 -07:00
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
@interface FPath ()
|
2017-05-15 12:27:07 -07:00
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
@property(nonatomic, readwrite, assign) NSInteger pieceNum;
|
|
|
|
|
@property(nonatomic, strong) NSArray *pieces;
|
2017-05-15 12:27:07 -07:00
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation FPath
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Initializers
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
+ (FPath *)relativePathFrom:(FPath *)outer to:(FPath *)inner {
|
|
|
|
|
NSString *outerFront = [outer getFront];
|
|
|
|
|
NSString *innerFront = [inner getFront];
|
2017-05-15 12:27:07 -07:00
|
|
|
if (outerFront == nil) {
|
|
|
|
|
return inner;
|
|
|
|
|
} else if ([outerFront isEqualToString:innerFront]) {
|
|
|
|
|
return [self relativePathFrom:[outer popFront] to:[inner popFront]];
|
|
|
|
|
} else {
|
2019-08-05 13:27:47 -07:00
|
|
|
@throw [[NSException alloc]
|
|
|
|
|
initWithName:@"FirebaseDatabaseInternalError"
|
|
|
|
|
reason:[NSString
|
|
|
|
|
stringWithFormat:
|
|
|
|
|
@"innerPath (%@) is not within outerPath (%@)",
|
|
|
|
|
inner, outer]
|
|
|
|
|
userInfo:nil];
|
2017-05-15 12:27:07 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
+ (FPath *)pathWithString:(NSString *)string {
|
2017-05-15 12:27:07 -07:00
|
|
|
return [[FPath alloc] initWith:string];
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (id)initWith:(NSString *)path {
|
2017-05-15 12:27:07 -07:00
|
|
|
self = [super init];
|
|
|
|
|
if (self) {
|
|
|
|
|
NSArray *pathPieces = [path componentsSeparatedByString:@"/"];
|
|
|
|
|
NSMutableArray *newPieces = [[NSMutableArray alloc] init];
|
|
|
|
|
for (NSInteger i = 0; i < pathPieces.count; i++) {
|
|
|
|
|
NSString *piece = [pathPieces objectAtIndex:i];
|
|
|
|
|
if (piece.length > 0) {
|
|
|
|
|
[newPieces addObject:piece];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.pieces = newPieces;
|
|
|
|
|
self.pieceNum = 0;
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)initWithPieces:(NSArray *)somePieces andPieceNum:(NSInteger)aPieceNum {
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self) {
|
|
|
|
|
self.pieceNum = aPieceNum;
|
|
|
|
|
self.pieces = somePieces;
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (id)copyWithZone:(NSZone *)zone {
|
2017-05-15 12:27:07 -07:00
|
|
|
// Immutable, so it's safe to return self
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)description {
|
|
|
|
|
return [self toString];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Public methods
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (NSString *)getFront {
|
|
|
|
|
if (self.pieceNum >= self.pieces.count) {
|
2017-05-15 12:27:07 -07:00
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
return [self.pieces objectAtIndex:self.pieceNum];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-08-05 13:27:47 -07:00
|
|
|
* @return The number of segments in this path
|
|
|
|
|
*/
|
|
|
|
|
- (NSUInteger)length {
|
2017-05-15 12:27:07 -07:00
|
|
|
return self.pieces.count - self.pieceNum;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (FPath *)popFront {
|
2017-05-15 12:27:07 -07:00
|
|
|
NSInteger newPieceNum = self.pieceNum;
|
|
|
|
|
if (newPieceNum < self.pieces.count) {
|
|
|
|
|
newPieceNum++;
|
|
|
|
|
}
|
|
|
|
|
return [[FPath alloc] initWithPieces:self.pieces andPieceNum:newPieceNum];
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (NSString *)getBack {
|
|
|
|
|
if (self.pieceNum < self.pieces.count) {
|
2017-05-15 12:27:07 -07:00
|
|
|
return [self.pieces lastObject];
|
2019-08-05 13:27:47 -07:00
|
|
|
} else {
|
2017-05-15 12:27:07 -07:00
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (NSString *)toString {
|
2017-05-15 12:27:07 -07:00
|
|
|
return [self toStringWithTrailingSlash:NO];
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (NSString *)toStringWithTrailingSlash {
|
2017-05-15 12:27:07 -07:00
|
|
|
return [self toStringWithTrailingSlash:YES];
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (NSString *)toStringWithTrailingSlash:(BOOL)trailingSlash {
|
|
|
|
|
NSMutableString *pathString = [[NSMutableString alloc] init];
|
|
|
|
|
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
2017-05-15 12:27:07 -07:00
|
|
|
[pathString appendString:@"/"];
|
|
|
|
|
[pathString appendString:[self.pieces objectAtIndex:i]];
|
|
|
|
|
}
|
|
|
|
|
if ([pathString length] == 0) {
|
|
|
|
|
return @"/";
|
|
|
|
|
} else {
|
|
|
|
|
if (trailingSlash) {
|
|
|
|
|
[pathString appendString:@"/"];
|
|
|
|
|
}
|
|
|
|
|
return pathString;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)wireFormat {
|
|
|
|
|
if ([self isEmpty]) {
|
|
|
|
|
return @"/";
|
|
|
|
|
} else {
|
2019-08-05 13:27:47 -07:00
|
|
|
NSMutableString *pathString = [[NSMutableString alloc] init];
|
2017-05-15 12:27:07 -07:00
|
|
|
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
|
|
|
|
if (i > self.pieceNum) {
|
|
|
|
|
[pathString appendString:@"/"];
|
|
|
|
|
}
|
|
|
|
|
[pathString appendString:[self.pieces objectAtIndex:i]];
|
|
|
|
|
}
|
|
|
|
|
return pathString;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (FPath *)parent {
|
|
|
|
|
if (self.pieceNum >= self.pieces.count) {
|
2017-05-15 12:27:07 -07:00
|
|
|
return nil;
|
|
|
|
|
} else {
|
2019-08-05 13:27:47 -07:00
|
|
|
NSMutableArray *newPieces = [[NSMutableArray alloc] init];
|
2017-05-15 12:27:07 -07:00
|
|
|
for (NSInteger i = self.pieceNum; i < self.pieces.count - 1; i++) {
|
|
|
|
|
[newPieces addObject:[self.pieces objectAtIndex:i]];
|
|
|
|
|
}
|
|
|
|
|
return [[FPath alloc] initWithPieces:newPieces andPieceNum:0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (FPath *)child:(FPath *)childPathObj {
|
|
|
|
|
NSMutableArray *newPieces = [[NSMutableArray alloc] init];
|
2017-05-15 12:27:07 -07:00
|
|
|
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
|
|
|
|
[newPieces addObject:[self.pieces objectAtIndex:i]];
|
|
|
|
|
}
|
2017-06-30 10:47:37 -07:00
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
for (NSInteger i = childPathObj.pieceNum; i < childPathObj.pieces.count;
|
|
|
|
|
i++) {
|
2017-05-15 12:27:07 -07:00
|
|
|
[newPieces addObject:[childPathObj.pieces objectAtIndex:i]];
|
|
|
|
|
}
|
2017-06-30 10:47:37 -07:00
|
|
|
|
2017-05-15 12:27:07 -07:00
|
|
|
return [[FPath alloc] initWithPieces:newPieces andPieceNum:0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (FPath *)childFromString:(NSString *)childPath {
|
2019-08-05 13:27:47 -07:00
|
|
|
NSMutableArray *newPieces = [[NSMutableArray alloc] init];
|
2017-05-15 12:27:07 -07:00
|
|
|
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
|
|
|
|
[newPieces addObject:[self.pieces objectAtIndex:i]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSArray *pathPieces = [childPath componentsSeparatedByString:@"/"];
|
|
|
|
|
for (unsigned int i = 0; i < pathPieces.count; i++) {
|
|
|
|
|
NSString *piece = [pathPieces objectAtIndex:i];
|
|
|
|
|
if (piece.length > 0) {
|
|
|
|
|
[newPieces addObject:piece];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [[FPath alloc] initWithPieces:newPieces andPieceNum:0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-08-05 13:27:47 -07:00
|
|
|
* @return True if there are no segments in this path
|
|
|
|
|
*/
|
|
|
|
|
- (BOOL)isEmpty {
|
2017-05-15 12:27:07 -07:00
|
|
|
return self.pieceNum >= self.pieces.count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-08-05 13:27:47 -07:00
|
|
|
* @return Singleton to represent an empty path
|
|
|
|
|
*/
|
|
|
|
|
+ (FPath *)empty {
|
2017-05-15 12:27:07 -07:00
|
|
|
static dispatch_once_t oneEmptyPath;
|
|
|
|
|
static FPath *emptyPath;
|
|
|
|
|
dispatch_once(&oneEmptyPath, ^{
|
2019-08-05 13:27:47 -07:00
|
|
|
emptyPath = [[FPath alloc] initWith:@""];
|
2017-05-15 12:27:07 -07:00
|
|
|
});
|
|
|
|
|
return emptyPath;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (BOOL)contains:(FPath *)other {
|
2017-05-15 12:27:07 -07:00
|
|
|
if (self.length > other.length) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSInteger i = self.pieceNum;
|
|
|
|
|
NSInteger j = other.pieceNum;
|
|
|
|
|
while (i < self.pieces.count) {
|
2019-08-05 13:27:47 -07:00
|
|
|
NSString *thisSeg = [self.pieces objectAtIndex:i];
|
|
|
|
|
NSString *otherSeg = [other.pieces objectAtIndex:j];
|
2017-05-15 12:27:07 -07:00
|
|
|
if (![thisSeg isEqualToString:otherSeg]) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
++i;
|
|
|
|
|
++j;
|
|
|
|
|
}
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (void)enumerateComponentsUsingBlock:(void (^)(NSString *, BOOL *))block {
|
2017-05-15 12:27:07 -07:00
|
|
|
BOOL stop = NO;
|
|
|
|
|
for (NSInteger i = self.pieceNum; !stop && i < self.pieces.count; i++) {
|
|
|
|
|
block(self.pieces[i], &stop);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (NSComparisonResult)compare:(FPath *)other {
|
2017-05-15 12:27:07 -07:00
|
|
|
NSInteger myCount = self.pieces.count;
|
|
|
|
|
NSInteger otherCount = other.pieces.count;
|
2019-08-05 13:27:47 -07:00
|
|
|
for (NSInteger i = self.pieceNum, j = other.pieceNum;
|
|
|
|
|
i < myCount && j < otherCount; i++, j++) {
|
|
|
|
|
NSComparisonResult comparison = [FUtilities compareKey:self.pieces[i]
|
|
|
|
|
toKey:other.pieces[j]];
|
2017-05-15 12:27:07 -07:00
|
|
|
if (comparison != NSOrderedSame) {
|
|
|
|
|
return comparison;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (self.length < other.length) {
|
|
|
|
|
return NSOrderedAscending;
|
|
|
|
|
} else if (other.length < self.length) {
|
|
|
|
|
return NSOrderedDescending;
|
|
|
|
|
} else {
|
2019-08-05 13:27:47 -07:00
|
|
|
NSAssert(self.length == other.length,
|
|
|
|
|
@"Paths must be the same lengths");
|
2017-05-15 12:27:07 -07:00
|
|
|
return NSOrderedSame;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-08-05 13:27:47 -07:00
|
|
|
* @return YES if paths are the same
|
|
|
|
|
*/
|
|
|
|
|
- (BOOL)isEqual:(id)other {
|
2017-05-15 12:27:07 -07:00
|
|
|
if (other == self) {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
if (!other || ![other isKindOfClass:[self class]]) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
FPath *otherPath = (FPath *)other;
|
|
|
|
|
if (self.length != otherPath.length) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
2019-08-05 13:27:47 -07:00
|
|
|
for (NSUInteger i = self.pieceNum, j = otherPath.pieceNum;
|
|
|
|
|
i < self.pieces.count; i++, j++) {
|
2017-05-15 12:27:07 -07:00
|
|
|
if (![self.pieces[i] isEqualToString:otherPath.pieces[j]]) {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-05 13:27:47 -07:00
|
|
|
- (NSUInteger)hash {
|
2017-05-15 12:27:07 -07:00
|
|
|
NSUInteger hashCode = 0;
|
|
|
|
|
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
|
|
|
|
hashCode = hashCode * 37 + [self.pieces[i] hash];
|
|
|
|
|
}
|
|
|
|
|
return hashCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|