1 package com.atlassian.httpclient.api;
2
3 import com.atlassian.util.concurrent.Promise;
4 import com.google.common.base.Function;
5
6 import javax.annotation.Nullable;
7
8 import static com.atlassian.httpclient.api.ResponsePromiseMapFunction.StatusRange;
9 import static com.google.common.base.Preconditions.checkNotNull;
10
11 public final class DefaultResponseTransformation<T> implements ResponseTransformation<T> {
12 private final ResponsePromiseMapFunction<T> mapFunctions;
13 private final Function<Throwable, ? extends T> failFunction;
14
15 private DefaultResponseTransformation(ResponsePromiseMapFunction<T> mapFunctions, Function<Throwable, ? extends T> failFunction) {
16 this.mapFunctions = mapFunctions;
17 this.failFunction = failFunction;
18 }
19
20 @Override
21 public Function<Throwable, ? extends T> getFailFunction() {
22 return failFunction;
23 }
24
25 @Override
26 public Function<Response, T> getSuccessFunctions() {
27 return mapFunctions;
28 }
29
30 @Override
31 public Promise<T> apply(final ResponsePromise responsePromise) {
32 return responsePromise.transform(this);
33 }
34
35 public static <T> Builder<T> builder() {
36 return new DefaultResponseTransformationBuilder<T>();
37 }
38
39 private static class DefaultResponseTransformationBuilder<T> implements Builder<T> {
40 private final ResponsePromiseMapFunction.ResponsePromiseMapFunctionBuilder<T> builder = ResponsePromiseMapFunction.builder();
41 private Function<Throwable, ? extends T> failFunction = defaultThrowableHandler();
42
43 @Override
44 public Builder<T> on(final HttpStatus status, final Function<Response, ? extends T> f) {
45 return addSingle(status, f);
46 }
47
48 @Override
49 public Builder<T> on(int statusCode, Function<Response, ? extends T> f) {
50 return addSingle(statusCode, f);
51 }
52
53 @Override
54 public Builder<T> informational(Function<Response, ? extends T> f) {
55 return addRange(HttpStatus.CONTINUE, f);
56 }
57
58
59 @Override
60 public Builder<T> successful(Function<Response, ? extends T> f) {
61 return addRange(HttpStatus.OK, f);
62 }
63
64 @Override
65 public Builder<T> ok(Function<Response, ? extends T> f) {
66 return addSingle(HttpStatus.OK, f);
67 }
68
69 @Override
70 public Builder<T> created(Function<Response, ? extends T> f) {
71 return addSingle(HttpStatus.CREATED, f);
72 }
73
74 @Override
75 public Builder<T> noContent(Function<Response, ? extends T> f) {
76 return addSingle(HttpStatus.NO_CONTENT, f);
77 }
78
79
80 @Override
81 public Builder<T> redirection(Function<Response, ? extends T> f) {
82 return addRange(HttpStatus.MULTIPLE_CHOICES, f);
83 }
84
85 @Override
86 public Builder<T> seeOther(Function<Response, ? extends T> f) {
87 return addSingle(HttpStatus.SEE_OTHER, f);
88 }
89
90 @Override
91 public Builder<T> notModified(Function<Response, ? extends T> f) {
92 return addSingle(HttpStatus.NOT_MODIFIED, f);
93 }
94
95
96 @Override
97 public Builder<T> clientError(Function<Response, ? extends T> f) {
98 return addRange(HttpStatus.BAD_REQUEST, f);
99 }
100
101 @Override
102 public Builder<T> badRequest(Function<Response, ? extends T> f) {
103 return addSingle(HttpStatus.BAD_REQUEST, f);
104 }
105
106 @Override
107 public Builder<T> unauthorized(Function<Response, ? extends T> f) {
108 return addSingle(HttpStatus.UNAUTHORIZED, f);
109 }
110
111 @Override
112 public Builder<T> forbidden(Function<Response, ? extends T> f) {
113 return addSingle(HttpStatus.FORBIDDEN, f);
114 }
115
116 @Override
117 public Builder<T> notFound(Function<Response, ? extends T> f) {
118 return addSingle(HttpStatus.NOT_FOUND, f);
119 }
120
121 @Override
122 public Builder<T> conflict(Function<Response, ? extends T> f) {
123 return addSingle(HttpStatus.CONFLICT, f);
124 }
125
126
127 @Override
128 public Builder<T> serverError(Function<Response, ? extends T> f) {
129 return addRange(HttpStatus.INTERNAL_SERVER_ERROR, f);
130 }
131
132 @Override
133 public Builder<T> internalServerError(Function<Response, ? extends T> f) {
134 return addSingle(HttpStatus.INTERNAL_SERVER_ERROR, f);
135 }
136
137 @Override
138 public Builder<T> serviceUnavailable(Function<Response, ? extends T> f) {
139 return addSingle(HttpStatus.SERVICE_UNAVAILABLE, f);
140 }
141
142
143 @Override
144 public Builder<T> error(Function<Response, ? extends T> f) {
145 builder.addStatusRangeFunction(
146 new OrStatusRange(new HundredsStatusRange(HttpStatus.BAD_REQUEST),
147 new HundredsStatusRange(HttpStatus.INTERNAL_SERVER_ERROR)), f);
148
149 return this;
150 }
151
152
153 @Override
154 public Builder<T> notSuccessful(Function<Response, ? extends T> f) {
155 builder.addStatusRangeFunction(new NotInStatusRange(new HundredsStatusRange(HttpStatus.OK)), f);
156 return this;
157 }
158
159 @Override
160 public Builder<T> others(Function<Response, ? extends T> f) {
161 builder.setOthersFunction(f);
162 return this;
163 }
164
165 @Override
166 public Builder<T> otherwise(final Function<Throwable, T> callback) {
167 others(new Function<Response, T>() {
168 @Override
169 public T apply(@Nullable Response input) {
170 return callback.apply(new UnexpectedResponseException(input));
171 }
172 });
173 fail(callback);
174 return this;
175 }
176
177 @Override
178 public Builder<T> done(final Function<Response, T> f) {
179 others(new Function<Response, T>() {
180 @Override
181 public T apply(@Nullable Response input) {
182 return f.apply(input);
183 }
184 });
185 return this;
186 }
187
188 @Override
189 public Builder<T> fail(Function<Throwable, ? extends T> f) {
190 this.failFunction = f;
191 return this;
192 }
193
194 private DefaultResponseTransformationBuilder<T> addSingle(HttpStatus status, Function<Response, ? extends T> f) {
195 return addSingle(status.code, f);
196 }
197
198 private DefaultResponseTransformationBuilder<T> addSingle(int statusCode, Function<Response, ? extends T> f) {
199 builder.addStatusRangeFunction(new SingleStatusRange(statusCode), f);
200 return this;
201 }
202
203 private DefaultResponseTransformationBuilder<T> addRange(HttpStatus status, Function<Response, ? extends T> f) {
204 builder.addStatusRangeFunction(new HundredsStatusRange(status), f);
205 return this;
206 }
207
208
209 private Function<Throwable, ? extends T> defaultThrowableHandler() {
210 return new Function<Throwable, T>() {
211 @Override
212 public T apply(Throwable throwable) {
213 if (throwable instanceof RuntimeException) {
214 throw (RuntimeException) throwable;
215 }
216 throw new ResponseTransformationException(throwable);
217 }
218 };
219 }
220
221 @Override
222 public ResponseTransformation<T> build() {
223 return new DefaultResponseTransformation<T>(builder.build(), failFunction);
224 }
225 }
226
227 static final class SingleStatusRange implements StatusRange {
228 private final int statusCode;
229
230 SingleStatusRange(int statusCode) {
231 this.statusCode = checkNotNull(statusCode);
232 }
233
234 @Override
235 public boolean isIn(int code) {
236 return this.statusCode == code;
237 }
238 }
239
240 static final class HundredsStatusRange implements StatusRange {
241 private final HttpStatus status;
242
243 private HundredsStatusRange(HttpStatus status) {
244 this.status = checkNotNull(status);
245 }
246
247 @Override
248 public boolean isIn(int code) {
249 final int diff = code - status.code;
250 return 0 <= diff && diff < 100;
251 }
252 }
253
254 static final class NotInStatusRange implements StatusRange {
255 private final StatusRange range;
256
257 private NotInStatusRange(StatusRange range) {
258 this.range = checkNotNull(range);
259 }
260
261 @Override
262 public boolean isIn(int code) {
263 return !range.isIn(code);
264 }
265 }
266
267 static final class OrStatusRange implements StatusRange {
268 private final StatusRange one;
269 private final StatusRange two;
270
271 private OrStatusRange(StatusRange one, StatusRange two) {
272 this.one = checkNotNull(one);
273 this.two = checkNotNull(two);
274 }
275
276 @Override
277 public boolean isIn(int code) {
278 return one.isIn(code) || two.isIn(code);
279 }
280 }
281 }