001/* 002 * flattr4j - A Java library for Flattr 003 * 004 * Copyright (C) 2011 Richard "Shred" Körber 005 * http://flattr4j.shredzone.org 006 * 007 * This program is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License / GNU Lesser 009 * General Public License as published by the Free Software Foundation, 010 * either version 3 of the License, or (at your option) any later version. 011 * 012 * Licensed under the Apache License, Version 2.0 (the "License"); 013 * you may not use this file except in compliance with the License. 014 * 015 * This program is distributed in the hope that it will be useful, 016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 018 * 019 */ 020package org.shredzone.flattr4j.impl; 021 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.List; 026 027import org.shredzone.flattr4j.FlattrService; 028import org.shredzone.flattr4j.connector.Connection; 029import org.shredzone.flattr4j.connector.Connector; 030import org.shredzone.flattr4j.connector.FlattrObject; 031import org.shredzone.flattr4j.connector.RateLimit; 032import org.shredzone.flattr4j.connector.RequestType; 033import org.shredzone.flattr4j.exception.FlattrException; 034import org.shredzone.flattr4j.model.Activity; 035import org.shredzone.flattr4j.model.AutoSubmission; 036import org.shredzone.flattr4j.model.Category; 037import org.shredzone.flattr4j.model.Flattr; 038import org.shredzone.flattr4j.model.Language; 039import org.shredzone.flattr4j.model.MiniThing; 040import org.shredzone.flattr4j.model.SearchQuery; 041import org.shredzone.flattr4j.model.SearchResult; 042import org.shredzone.flattr4j.model.Submission; 043import org.shredzone.flattr4j.model.Subscription; 044import org.shredzone.flattr4j.model.Thing; 045import org.shredzone.flattr4j.model.ThingId; 046import org.shredzone.flattr4j.model.User; 047import org.shredzone.flattr4j.model.UserId; 048 049/** 050 * Default implementation of {@link FlattrService}. 051 * 052 * @author Richard "Shred" Körber 053 */ 054public class FlattrServiceImpl implements FlattrService { 055 private final Connector connector; 056 057 private RateLimit lastRateLimit = new RateLimit(); 058 private boolean fullMode = false; 059 060 public FlattrServiceImpl(Connector connector) { 061 this.connector = connector; 062 } 063 064 /** 065 * Returns the {@link Connector} used for calling the API. 066 * 067 * @return {@link Connector} 068 */ 069 protected Connector getConnector() { 070 return connector; 071 } 072 073 @Override 074 public void setFullMode(boolean full) { 075 this.fullMode = full; 076 } 077 078 @Override 079 public boolean isFullMode() { 080 return fullMode; 081 } 082 083 @Override 084 public ThingId create(Submission thing) throws FlattrException { 085 if (thing == null) 086 throw new IllegalArgumentException("thing is required"); 087 088 if (thing instanceof AutoSubmission && ((AutoSubmission) thing).getUser() != null) 089 throw new IllegalArgumentException("cannot create a thing on behalf of a user"); 090 091 FlattrObject data = getConnector().create(RequestType.POST) 092 .call("things") 093 .data(thing.toFlattrObject()) 094 .rateLimit(lastRateLimit) 095 .singleResult(); 096 097 return Thing.withId(String.valueOf(data.getInt("id"))); 098 } 099 100 @Override 101 public void update(Thing thing) throws FlattrException { 102 if (thing == null) 103 throw new IllegalArgumentException("thing is required"); 104 105 FlattrObject update = thing.toUpdate(); 106 107 if (update != null) { // Thing was modified. 108 update.getJSONObject().put("_method", "patch"); 109 getConnector().create(RequestType.POST) 110 .call("things/:id") 111 .parameter("id", thing.getThingId()) 112 .rateLimit(lastRateLimit) 113 .data(update) 114 .result(); 115 } 116 } 117 118 @Override 119 public void delete(ThingId thingId) throws FlattrException { 120 if (thingId == null || thingId.getThingId().length() == 0) 121 throw new IllegalArgumentException("thing id is required"); 122 123 getConnector().create(RequestType.DELETE) 124 .call("things/:id") 125 .parameter("id", thingId.getThingId()) 126 .rateLimit(lastRateLimit) 127 .result(); 128 } 129 130 @Override 131 public MiniThing flattr(AutoSubmission submission) throws FlattrException { 132 return flattr(submission.toUrl()); 133 } 134 135 @Override 136 public MiniThing flattr(String url) throws FlattrException { 137 if (url == null || url.length() == 0) 138 throw new IllegalArgumentException("url is required"); 139 140 FlattrObject data = new FlattrObject(); 141 data.put("url", url); 142 143 FlattrObject result = getConnector().create(RequestType.POST) 144 .call("flattr") 145 .data(data) 146 .rateLimit(lastRateLimit) 147 .singleResult(); 148 149 return new MiniThing(result.getFlattrObject("thing")); 150 } 151 152 @Override 153 public MiniThing flattr(ThingId thingId) throws FlattrException { 154 if (thingId == null || thingId.getThingId().length() == 0) 155 throw new IllegalArgumentException("thingId is required"); 156 157 FlattrObject result = getConnector().create(RequestType.POST) 158 .call("things/:id/flattr") 159 .parameter("id", thingId.getThingId()) 160 .rateLimit(lastRateLimit) 161 .singleResult(); 162 163 return new MiniThing(result.getFlattrObject("thing")); 164 } 165 166 @Override 167 public User getMyself() throws FlattrException { 168 return new User(getConnector().create() 169 .call("user") 170 .rateLimit(lastRateLimit) 171 .singleResult()); 172 } 173 174 @Override 175 public List<Thing> getMyThings() throws FlattrException { 176 return getMyThings(null, null); 177 } 178 179 @Override 180 public List<Thing> getMyThings(Integer count, Integer page) throws FlattrException { 181 Connection conn = getConnector().create() 182 .call("user/things") 183 .rateLimit(lastRateLimit); 184 185 setupFullMode(conn); 186 187 if (count != null) { 188 conn.query("count", count.toString()); 189 } 190 191 if (page != null) { 192 conn.query("page", page.toString()); 193 } 194 195 List<Thing> list = new ArrayList<Thing>(); 196 for (FlattrObject data : conn.result()) { 197 list.add(new Thing(data)); 198 } 199 return Collections.unmodifiableList(list); 200 } 201 202 @Override 203 public List<Flattr> getMyFlattrs() throws FlattrException { 204 return getMyFlattrs(null, null); 205 } 206 207 @Override 208 public List<Flattr> getMyFlattrs(Integer count, Integer page) throws FlattrException { 209 Connection conn = getConnector().create() 210 .call("user/flattrs") 211 .rateLimit(lastRateLimit); 212 213 setupFullMode(conn); 214 215 if (count != null) { 216 conn.query("count", count.toString()); 217 } 218 219 if (page != null) { 220 conn.query("page", page.toString()); 221 } 222 223 List<Flattr> list = new ArrayList<Flattr>(); 224 for (FlattrObject data : conn.result()) { 225 list.add(new Flattr(data)); 226 } 227 return Collections.unmodifiableList(list); 228 } 229 230 @Override 231 public Thing getThing(ThingId thingId) throws FlattrException { 232 if (thingId == null || thingId.getThingId().length() == 0) 233 throw new IllegalArgumentException("thingId is required"); 234 235 Connection conn = getConnector().create() 236 .call("things/:id") 237 .parameter("id", thingId.getThingId()) 238 .rateLimit(lastRateLimit); 239 240 setupFullMode(conn); 241 242 return new Thing(conn.singleResult()); 243 } 244 245 @Override 246 public Thing getThingByUrl(String url) throws FlattrException { 247 if (url == null || url.length() == 0) 248 throw new IllegalArgumentException("url is required"); 249 250 FlattrObject data = getConnector().create() 251 .call("things/lookup/") 252 .query("url", url) 253 .rateLimit(lastRateLimit) 254 .singleResult(); 255 256 if (data.has("message") && "not_found".equals(data.get("message"))) { 257 return null; 258 } 259 260 return new Thing(data); 261 } 262 263 @Override 264 public Thing getThingBySubmission(AutoSubmission submission) throws FlattrException { 265 return getThingByUrl(submission.toUrl()); 266 } 267 268 @Override 269 public List<Thing> getThings(UserId user) throws FlattrException { 270 return getThings(user, null, null); 271 } 272 273 @Override 274 public List<Thing> getThings(UserId user, Integer count, Integer page) throws FlattrException { 275 if (user == null || user.getUserId().length() == 0) 276 throw new IllegalArgumentException("user is required"); 277 278 Connection conn = getConnector().create() 279 .call("users/:username/things") 280 .parameter("username", user.getUserId()) 281 .rateLimit(lastRateLimit); 282 283 setupFullMode(conn); 284 285 if (count != null) { 286 conn.query("count", count.toString()); 287 } 288 289 if (page != null) { 290 conn.query("page", page.toString()); 291 } 292 293 List<Thing> list = new ArrayList<Thing>(); 294 for (FlattrObject data : conn.result()) { 295 list.add(new Thing(data)); 296 } 297 return Collections.unmodifiableList(list); 298 } 299 300 @Override 301 public List<Thing> getThings(Collection<? extends ThingId> thingIds) throws FlattrException { 302 if (thingIds.isEmpty()) { 303 // No IDs, so the result will be empty anyways 304 return Collections.emptyList(); 305 } 306 307 String[] params = new String[thingIds.size()]; 308 int ix = 0; 309 for (ThingId thingId : thingIds) { 310 params[ix++] = thingId.getThingId(); 311 } 312 313 Connection conn = getConnector().create() 314 .call("things/:ids") 315 .parameterArray("ids", params) 316 .rateLimit(lastRateLimit); 317 318 setupFullMode(conn); 319 320 List<Thing> list = new ArrayList<Thing>(); 321 for (FlattrObject data : conn.result()) { 322 list.add(new Thing(data)); 323 } 324 return Collections.unmodifiableList(list); 325 } 326 327 @Override 328 public SearchResult searchThings(SearchQuery query, Integer count, Integer page) throws FlattrException { 329 Connection conn = getConnector().create() 330 .call("things/search") 331 .rateLimit(lastRateLimit); 332 333 if (query != null) { 334 query.setupConnection(conn); 335 } 336 337 setupFullMode(conn); 338 339 if (count != null) { 340 conn.query("count", count.toString()); 341 } 342 343 if (page != null) { 344 conn.query("page", page.toString()); 345 } 346 347 return new SearchResult(conn.singleResult()); 348 } 349 350 @Override 351 public User getUser(UserId user) throws FlattrException { 352 if (user == null || user.getUserId().length() == 0) 353 throw new IllegalArgumentException("user is required"); 354 355 return new User(getConnector().create() 356 .call("users/:username") 357 .parameter("username", user.getUserId()) 358 .rateLimit(lastRateLimit) 359 .singleResult()); 360 } 361 362 @Override 363 public List<Flattr> getFlattrs(UserId user) throws FlattrException { 364 return getFlattrs(user, null, null); 365 } 366 367 @Override 368 public List<Flattr> getFlattrs(UserId userId, Integer count, Integer page) throws FlattrException { 369 if (userId == null || userId.getUserId().length() == 0) 370 throw new IllegalArgumentException("userId is required"); 371 372 Connection conn = getConnector().create() 373 .call("users/:username/flattrs") 374 .parameter("username", userId.getUserId()) 375 .rateLimit(lastRateLimit); 376 377 setupFullMode(conn); 378 379 if (count != null) { 380 conn.query("count", count.toString()); 381 } 382 383 if (page != null) { 384 conn.query("page", page.toString()); 385 } 386 387 List<Flattr> list = new ArrayList<Flattr>(); 388 for (FlattrObject data : conn.result()) { 389 list.add(new Flattr(data)); 390 } 391 return Collections.unmodifiableList(list); 392 } 393 394 @Override 395 public List<Flattr> getFlattrs(ThingId thingId) throws FlattrException { 396 return getFlattrs(thingId, null, null); 397 } 398 399 @Override 400 public List<Flattr> getFlattrs(ThingId thingId, Integer count, Integer page) throws FlattrException { 401 if (thingId == null || thingId.getThingId().length() == 0) 402 throw new IllegalArgumentException("thingId is required"); 403 404 Connection conn = getConnector().create() 405 .call("things/:id/flattrs") 406 .parameter("id", thingId.getThingId()) 407 .rateLimit(lastRateLimit); 408 409 setupFullMode(conn); 410 411 if (count != null) { 412 conn.query("count", count.toString()); 413 } 414 415 if (page != null) { 416 conn.query("page", page.toString()); 417 } 418 419 List<Flattr> list = new ArrayList<Flattr>(); 420 for (FlattrObject data : conn.result()) { 421 list.add(new Flattr(data)); 422 } 423 return Collections.unmodifiableList(list); 424 } 425 426 @Override 427 public List<Activity> getActivities(UserId user, Activity.Type type) throws FlattrException { 428 if (user == null || user.getUserId().length() == 0) 429 throw new IllegalArgumentException("userId is required"); 430 431 Connection conn = getConnector().create() 432 .call("users/:username/activities.as") 433 .parameter("username", user.getUserId()) 434 .rateLimit(lastRateLimit); 435 436 if (type != null) { 437 conn.query("type", type.name().toLowerCase()); 438 } 439 440 FlattrObject data = conn.singleResult(); 441 List<Activity> list = new ArrayList<Activity>(); 442 for (FlattrObject item : data.getObjects("items")) { 443 list.add(new Activity(item)); 444 } 445 return Collections.unmodifiableList(list); 446 } 447 448 @Override 449 public List<Activity> getMyActivities(Activity.Type type) throws FlattrException { 450 Connection conn = getConnector().create() 451 .call("user/activities.as") 452 .rateLimit(lastRateLimit); 453 454 if (type != null) { 455 conn.query("type", type.name().toLowerCase()); 456 } 457 458 FlattrObject data = conn.singleResult(); 459 List<Activity> list = new ArrayList<Activity>(); 460 for (FlattrObject item : data.getObjects("items")) { 461 list.add(new Activity(item)); 462 } 463 return Collections.unmodifiableList(list); 464 } 465 466 @Override 467 public List<Subscription> getMySubscriptions() throws FlattrException { 468 Connection conn = getConnector().create() 469 .call("user/subscriptions") 470 .rateLimit(lastRateLimit); 471 472 List<Subscription> list = new ArrayList<Subscription>(); 473 for (FlattrObject item : conn.result()) { 474 list.add(new Subscription(item)); 475 } 476 return Collections.unmodifiableList(list); 477 } 478 479 @Override 480 public Subscription getSubscription(ThingId thingId) throws FlattrException { 481 if (thingId == null || thingId.getThingId().length() == 0) { 482 throw new IllegalArgumentException("thingId is required"); 483 } 484 485 Connection conn = getConnector().create() 486 .call("user/subscriptions") 487 .rateLimit(lastRateLimit); 488 489 for (FlattrObject item : conn.result()) { 490 if (thingId.getThingId().equals(String.valueOf(item.getFlattrObject("thing").getInt("id")))) { 491 return new Subscription(item); 492 } 493 } 494 495 return null; 496 } 497 498 @Override 499 public void subscribe(ThingId thingId) throws FlattrException { 500 if (thingId == null || thingId.getThingId().length() == 0) 501 throw new IllegalArgumentException("thingId is required"); 502 503 getConnector().create(RequestType.POST) 504 .call("things/:id/subscriptions") 505 .parameter("id", thingId.getThingId()) 506 .rateLimit(lastRateLimit) 507 .result(); 508 } 509 510 @Override 511 public void unsubscribe(ThingId thingId) throws FlattrException { 512 if (thingId == null || thingId.getThingId().length() == 0) 513 throw new IllegalArgumentException("thingId is required"); 514 515 getConnector().create(RequestType.DELETE) 516 .call("things/:id/subscriptions") 517 .parameter("id", thingId.getThingId()) 518 .rateLimit(lastRateLimit) 519 .result(); 520 } 521 522 @Override 523 public boolean toggleSubscription(ThingId thingId) throws FlattrException { 524 if (thingId == null || thingId.getThingId().length() == 0) 525 throw new IllegalArgumentException("thingId is required"); 526 527 FlattrObject data = getConnector().create(RequestType.PUT) 528 .call("things/:id/subscriptions") 529 .parameter("id", thingId.getThingId()) 530 .rateLimit(lastRateLimit) 531 .singleResult(); 532 533 return "paused".equals(data.get("message")); 534 } 535 536 @Override 537 public void pauseSubscription(ThingId thingId, boolean paused) throws FlattrException { 538 boolean current = toggleSubscription(thingId); 539 540 if (current != paused) { 541 toggleSubscription(thingId); 542 } 543 } 544 545 @Override 546 public List<Category> getCategories() throws FlattrException { 547 Connection conn = getConnector().create() 548 .call("categories") 549 .rateLimit(lastRateLimit); 550 551 List<Category> list = new ArrayList<Category>(); 552 for (FlattrObject data : conn.result()) { 553 list.add(new Category(data)); 554 } 555 return Collections.unmodifiableList(list); 556 } 557 558 @Override 559 public List<Language> getLanguages() throws FlattrException { 560 Connection conn = getConnector().create() 561 .call("languages") 562 .rateLimit(lastRateLimit); 563 564 List<Language> list = new ArrayList<Language>(); 565 for (FlattrObject data : conn.result()) { 566 list.add(new Language(data)); 567 } 568 return Collections.unmodifiableList(list); 569 } 570 571 @Override 572 public RateLimit getCurrentRateLimit() throws FlattrException { 573 Connection conn = getConnector().create() 574 .call("rate_limit"); 575 576 return new RateLimit(conn.singleResult()); 577 } 578 579 @Override 580 public RateLimit getLastRateLimit() { 581 return lastRateLimit; 582 } 583 584 /** 585 * Sets the {@link Connection} according to the current full mode. 586 * 587 * @param conn 588 * {@link Connection} to set 589 */ 590 protected void setupFullMode(Connection conn) { 591 if (fullMode) { 592 conn.query("full", "1"); 593 } 594 } 595 596}