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 */ 019package org.shredzone.flattr4j.model; 020 021import java.util.Date; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Set; 025 026import org.shredzone.flattr4j.FlattrService; 027import org.shredzone.flattr4j.connector.FlattrObject; 028import org.shredzone.flattr4j.exception.MarshalException; 029 030/** 031 * A {@link Thing} that has been registered with Flattr. Two {@link Thing} are considered 032 * equal if they contain the same id. Some properties of a {@link Thing} can be modified 033 * by setters. After that, invoke 034 * {@link FlattrService#update(org.shredzone.flattr4j.model.Thing)} to persist the 035 * changes. 036 * <p> 037 * This class is not threadsafe. 038 * 039 * @author Richard "Shred" Körber 040 */ 041public class Thing extends Resource implements ThingId, UserId, CategoryId, LanguageId { 042 private static final long serialVersionUID = 2822280427303390055L; 043 044 private transient List<String> tags = null; 045 private transient User user = null; 046 private Set<String> updatedKeys = new HashSet<String>(); 047 048 /** 049 * Returns a {@link ThingId} for the given Thing id. 050 * 051 * @param id 052 * Thing id 053 * @return A {@link ThingId} object for this id 054 */ 055 public static ThingId withId(final String id) { 056 return new ThingId() { 057 @Override 058 public String getThingId() { 059 return id; 060 } 061 }; 062 } 063 064 public Thing(FlattrObject data) { 065 super(data); 066 } 067 068 /** 069 * Thing's unique id at Flattr. 070 */ 071 @Override 072 public String getThingId() { 073 return String.valueOf(data.getInt("id")); 074 } 075 076 /** 077 * URL that returns details of this resource as JSON. 078 */ 079 public String getResource() { 080 return data.get("resource"); 081 } 082 083 /** 084 * Human readable link to this resource at Flattr. 085 */ 086 public String getLink() { 087 return data.get("link"); 088 } 089 090 /** 091 * Creation date of the Thing. 092 */ 093 public Date getCreated() { 094 return data.getDate("created_at"); 095 } 096 097 /** 098 * How many times this Thing was flattred. 099 */ 100 public int getClicks() { 101 return data.getInt("flattrs"); 102 } 103 104 /** 105 * URL of the Thing. 106 */ 107 public String getUrl() { 108 return data.get("url"); 109 } 110 111 /** 112 * Title of the Thing. 113 */ 114 public String getTitle() { 115 return data.get("title"); 116 } 117 118 public void setTitle(String title) { 119 data.put("title", title); 120 updatedKeys.add("title"); 121 } 122 123 /** 124 * URL of an image for this Thing. Empty string if there is none. 125 * 126 * @since 2.0 127 */ 128 public String getImage() { 129 return (data.has("image") ? data.get("image") : ""); 130 } 131 132 /** 133 * User this Thing belongs to. 134 */ 135 @Override 136 public String getUserId() { 137 return data.getSubString("owner", "username"); 138 } 139 140 /** 141 * User this Thing belongs to. 142 * <p> 143 * All properties are only available when the service was set to full mode! 144 * 145 * @since 2.2 146 */ 147 public User getUser() { 148 if (user == null) { 149 user = new User(data.getFlattrObject("owner")); 150 } 151 return user; 152 } 153 154 /** 155 * Category this Thing belongs to. 156 */ 157 @Override 158 public String getCategoryId() { 159 return data.get("category"); 160 } 161 162 public void setCategory(CategoryId category) { 163 data.put("category", (category != null ? category.getCategoryId() : null)); 164 updatedKeys.add("category"); 165 } 166 167 /** 168 * A descriptive text about the Thing. 169 */ 170 public String getDescription() { 171 return data.get("description"); 172 } 173 174 public void setDescription(String description) { 175 data.put("description", description); 176 updatedKeys.add("description"); 177 } 178 179 /** 180 * Tags this Thing is tagged with. 181 */ 182 public List<String> getTags() { 183 if (tags == null) { 184 tags = data.getStrings("tags"); 185 } 186 return tags; 187 } 188 189 public void setTags(List<String> tags) { 190 this.tags = tags; 191 updatedKeys.add("tags"); 192 } 193 194 public void addTag(String tag) { 195 if (tags == null) { 196 tags = data.getStrings("tags"); 197 } 198 tags.add(tag); 199 updatedKeys.add("tags"); 200 } 201 202 /** 203 * Language id of the Thing. 204 */ 205 @Override 206 public String getLanguageId() { 207 return data.get("language"); 208 } 209 210 public void setLanguage(LanguageId language) { 211 data.put("language", (language != null ? language.getLanguageId() : null)); 212 updatedKeys.add("language"); 213 } 214 215 /** 216 * Is the Thing hidden from the public list of Things at Flattr? 217 */ 218 public boolean isHidden() { 219 return data.getBoolean("hidden"); 220 } 221 222 public void setHidden(boolean hidden) { 223 data.put("hidden", hidden); 224 updatedKeys.add("hidden"); 225 } 226 227 /** 228 * Is this Thing flattred? 229 * 230 * @since 2.0 231 */ 232 public boolean isFlattred() { 233 return data.getBoolean("flattred"); 234 } 235 236 /** 237 * Is this Thing subscribed? 238 * 239 * @since 2.6 240 */ 241 public boolean isSubscribed() { 242 return data.getBoolean("subscribed"); 243 } 244 245 /** 246 * Date of last Flattr. Only available to the owner of this Thing. 247 * 248 * @since 2.0 249 */ 250 public Date getLastFlattr() { 251 return data.getDate("last_flattr_at"); 252 } 253 254 /** 255 * Date of last Update. Only available to the owner of this Thing. 256 * 257 * @since 2.0 258 */ 259 public Date getUpdated() { 260 return data.getDate("updated_at"); 261 } 262 263 /** 264 * Merges the contents of a submission to this {@link Thing}. This method is useful if 265 * you want to modify an existing {@link Thing} by a {@link Submission} object. 266 * <p> 267 * For all unset properties of {@link Submission}, the {@link Thing} properties are 268 * cleared. After merging, the {@link Thing} is in a state as if it was created with 269 * the given {@link Submission}. If you only want to change single properties of 270 * {@link Thing}, use the setters instead. 271 * <p> 272 * <em>NOTE:</em> The URL of a {@link Thing} cannot be changed. The {@link Submission} 273 * object must contain either this {@link Thing}'s URL or {@code null} (for 274 * convenience). Otherwise an {@link IllegalArgumentException} is thrown. 275 * 276 * @param submission 277 * {@link Submission} to merge 278 * @since 2.0 279 */ 280 public void merge(Submission submission) { 281 if (submission.getUrl() != null && !this.getUrl().equals(submission.getUrl())) { 282 throw new IllegalArgumentException("Thing URL '" + getUrl() 283 + "' cannot be changed to '" + submission.getUrl() + "'"); 284 } 285 286 this.setCategory(submission.getCategory()); 287 this.setTitle(submission.getTitle()); 288 this.setDescription(submission.getDescription()); 289 this.setLanguage(submission.getLanguage()); 290 this.setTags(submission.getTags()); 291 292 if (submission.isHidden() != null) { 293 this.setHidden(submission.isHidden()); 294 } 295 } 296 297 /** 298 * Returns a {@link FlattrObject} for the updates that have been applied to this 299 * Thing. 300 * 301 * @return {@link FlattrObject} for the updates, or {@code null} if this Thing was not 302 * modified. 303 * @since 2.0 304 */ 305 public FlattrObject toUpdate() { 306 if (updatedKeys.isEmpty()) { 307 return null; 308 } 309 310 data.putStrings("tags", tags); 311 312 FlattrObject result = new FlattrObject(); 313 for (String key : updatedKeys) { 314 if ("tags".equals(key)) { 315 // Strangely, tags are expected to be joined in a comma separated string 316 StringBuilder sb = new StringBuilder(); 317 for (String tag : tags) { 318 if (tag.indexOf(',') >= 0) { 319 throw new MarshalException("tag '" + tag + "' contains invalid character ','"); 320 } 321 sb.append(',').append(tag); 322 } 323 if (sb.length() > 0) { 324 sb.deleteCharAt(0); 325 } 326 result.put(key, sb.toString()); 327 } else { 328 if (data.has(key)) { 329 result.put(key, data.getObject(key)); 330 } 331 } 332 } 333 return result; 334 } 335 336 /** 337 * Returns the URL of a PDF document containing a QR code of the Thing. The PDF 338 * can be printed, sticked on the wall, and then flattered using a mobile phone. 339 */ 340 public String getQrPdfUrl() { 341 return "https://flattr.com/thing/qr/" + getThingId(); 342 } 343 344 @Override 345 public boolean equals(Object obj) { 346 String pk = getThingId(); 347 if (pk == null || obj == null || !(obj instanceof Thing)) { 348 return false; 349 } 350 return pk.equals(((Thing) obj).getThingId()); 351 } 352 353 @Override 354 public int hashCode() { 355 String pk = getThingId(); 356 return (pk != null ? pk.hashCode() : 0); 357 } 358 359}