blob: 7108a565846d12a2159230f6f31c4ab0de060337 [file] [log] [blame]
Jorge Canizalese8304d52015-02-17 19:50:51 -08001/*
2 *
Yang Gao5fc90292015-02-20 09:46:22 -08003 * Copyright 2015, Google Inc.
Jorge Canizalese8304d52015-02-17 19:50:51 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
Jorge Canizales30697c92015-02-17 17:09:14 -080034#import "GRXImmediateWriter.h"
35
36#import "NSEnumerator+GRXUtil.h"
37
38@implementation GRXImmediateWriter {
39 NSEnumerator *_enumerator;
40 NSError *_errorOrNil;
41 id<GRXWriteable> _writeable;
42}
43
44@synthesize state = _state;
45
46- (instancetype) init {
47 return [self initWithEnumerator:nil error:nil]; // results in an empty writer.
48}
49
50// Designated initializer
51- (instancetype)initWithEnumerator:(NSEnumerator *)enumerator error:(NSError *)errorOrNil {
52 if (((self = [super init]))) {
53 _enumerator = enumerator;
54 _errorOrNil = errorOrNil;
55 _state = GRXWriterStateNotStarted;
56 }
57 return self;
58}
59
60#pragma mark Convenience constructors
61
62+ (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator error:(NSError *)errorOrNil {
63 return [[self alloc] initWithEnumerator:enumerator error:errorOrNil];
64}
65
66+ (id<GRXWriter>)writerWithEnumerator:(NSEnumerator *)enumerator {
67 return [self writerWithEnumerator:enumerator error:nil];
68}
69
70+ (id<GRXWriter>)writerWithValueSupplier:(id (^)())block {
71 return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithValueSupplier:block]];
72}
73
74+ (id<GRXWriter>)writerWithContainer:(id<NSFastEnumeration>)container {
75 return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithContainer:container]];;
76}
77
78+ (id<GRXWriter>)writerWithValue:(id)value {
79 if (value) {
80 return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithSingleValue:value]];
81 } else {
82 return [self emptyWriter];
83 }
84}
85
86+ (id<GRXWriter>)writerWithError:(NSError *)error {
87 if (error) {
88 return [self writerWithEnumerator:nil error:error];
89 } else {
90 return [self emptyWriter];
91 }
92}
93
94+ (id<GRXWriter>)emptyWriter {
95 static GRXImmediateWriter *emptyWriter;
96 static dispatch_once_t onceToken;
97 dispatch_once(&onceToken, ^{
98 emptyWriter = [self writerWithEnumerator:nil error:nil];
99 });
100 return emptyWriter;
101}
102
103#pragma mark Conformance with GRXWriter
104
105// Most of the complexity in this implementation is the result of supporting pause and resumption of
106// the GRXWriter. It's an important feature for instances of GRXWriter that are backed by a
107// container (which may be huge), or by a NSEnumerator (which may even be infinite).
108
109- (void)writeUntilPausedOrStopped {
110 id value;
111 while (value = [_enumerator nextObject]) {
Jorge Canizalesa90a9c32015-05-18 17:12:41 -0700112 [_writeable writeValue:value];
Jorge Canizales30697c92015-02-17 17:09:14 -0800113 // If the writeable has a reference to us, it might change our state to paused or finished.
114 if (_state == GRXWriterStatePaused || _state == GRXWriterStateFinished) {
115 return;
116 }
117 }
118 [self finishWithError:_errorOrNil];
119}
120
121- (void)startWithWriteable:(id<GRXWriteable>)writeable {
122 _state = GRXWriterStateStarted;
123 _writeable = writeable;
124 [self writeUntilPausedOrStopped];
125}
126
127- (void)finishWithError:(NSError *)errorOrNil {
128 _state = GRXWriterStateFinished;
129 _enumerator = nil;
130 _errorOrNil = nil;
131 id<GRXWriteable> writeable = _writeable;
132 _writeable = nil;
133 [writeable didFinishWithError:errorOrNil];
134}
135
136- (void)setState:(GRXWriterState)newState {
137 // Manual transitions are only allowed from the started or paused states.
138 if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) {
139 return;
140 }
141
142 switch (newState) {
143 case GRXWriterStateFinished:
144 _state = newState;
145 _enumerator = nil;
146 _errorOrNil = nil;
147 // Per GRXWriter's contract, setting the state to Finished manually
148 // means one doesn't wish the writeable to be messaged anymore.
149 _writeable = nil;
150 return;
151 case GRXWriterStatePaused:
152 _state = newState;
153 return;
154 case GRXWriterStateStarted:
155 if (_state == GRXWriterStatePaused) {
156 _state = newState;
157 [self writeUntilPausedOrStopped];
158 }
159 return;
160 case GRXWriterStateNotStarted:
161 return;
162 }
163}
164
165@end