/** * Copyright 2013 OpenSocial Foundation * Copyright 2013 International Business Machines Corporation * * 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. * * Utility library for working with Activity Streams Actions * Requires underscorejs. * * @author James M Snell (jasnell@us.ibm.com) */ package com.ibm.common.activitystreams; import static com.google.common.base.Preconditions.checkArgument; import static com.ibm.common.activitystreams.Makers.linkValue; import static com.google.common.collect.Iterables.filter; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.concurrent.TimeUnit; import org.joda.time.DateTime; import org.joda.time.ReadableDuration; import org.joda.time.ReadablePeriod; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; /** * * An Activity Streams collection object * * <pre> * import static com.ibm.common.activitystreams.Makers.collection; * import static com.ibm.common.activitystreams.Makers.object; * ... * * Collection collection = collection() * .items(object()...) * .items(object()...) * .get(); * </pre> * * @author james * @version $Revision: 1.0 $ */ @SuppressWarnings("unchecked") public class Collection extends ASObject implements Serializable { /** Standardized Paging Links **/ public static enum Page { /** * Reference to the first page in the logical set **/ FIRST, /** * Reference to the next page in the logical set **/ NEXT, /** * Reference to the final page in the logical set * **/ LAST, /** * Reference to the previous page in the logical set **/ PREVIOUS, /** * Reference to the previous page in the logical set (treated as an alias for "previous") * @deprecated Use Page.PREVIOUS instead **/ PREV, /** * Reference to the page containing the most recently published/updated * items in the logical set **/ CURRENT, /** * Reference to this page **/ SELF; private final String label; private Page() { this.label = name().toLowerCase(); } private String checkPrev(Collection col, String or) { if (this == PREVIOUS) { if (!col.has(label) && col.has(PREV.label)) return PREV.label; } else if (this == PREV) { if (!col.has(label) && col.has(PREVIOUS.label)) return PREVIOUS.label; } return or; } private Iterable<LinkValue> links(Collection col) { return col.links(checkPrev(col,label)); } private Iterable<LinkValue> links( Collection col, Predicate<? super LinkValue> filter) { return col.links(checkPrev(col,label), filter); } private LinkValue first(Collection col) { return col.firstLink(checkPrev(col,label)); } private LinkValue firstMatching( Collection col, Predicate<? super LinkValue> filter) { return col.firstMatchingLink(checkPrev(col,label), filter); } } /** * @author james * @version $Revision: 1.0 $ */ public static final class Builder extends Collection.AbstractBuilder<Collection,Collection.Builder> { /** * Method create. * @return Collection */ protected Collection create() { return new Collection(this); } } /** * @author james * @version $Revision: 1.0 $ */ public static abstract class AbstractBuilder <A extends Collection, B extends Collection.AbstractBuilder<A,B>> extends ASObject.AbstractBuilder<A, B> { protected final ImmutableList.Builder<ASObject> list = ImmutableList.builder(); /** * Method create. * @return A **/ protected abstract A create(); @Override public B set(String key, Object value) { if (key.equals("items")) { if (value instanceof ArrayLinkValue) { ArrayLinkValue alv = (ArrayLinkValue) value; for (LinkValue lv : alv) { list.add((ASObject)lv); } } else if (value instanceof ASObject) list.add((ASObject) value); return (B)this; } else return super.set(key,value); } /** * Method get. * @return A * @see com.google.common.base.Supplier#get() **/ public A get() { super.set("items", list.build()); return create(); } /** * Add items to this collection * @param objs java.util.Iterable<? extends ASObject> * @return B */ public B items(Iterable<? extends ASObject> objs) { if (objs == null) return (B)this; for (ASObject obj : objs) items(obj); return (B)this; } /** * Add items to this collection * @param obj ASObject The object to add * @param objs ASObject[] Additional objects to add (vararg) * @return B **/ public B items(ASObject obj, ASObject... objs) { if (obj == null) return (B)this; list.add(obj); if (objs != null) for (ASObject o : objs) list.add(o); return (B)this; } /** * Add an item to this collection * @param obj com.google.common.base.Supplier<? extends ASObject> * @return B **/ public B items(Supplier<? extends ASObject> obj) { if (obj == null) return (B)this; items(obj.get()); return (B)this; } /** * Set the total number of items (must be non-negative) * @param i int * @return B **/ public B totalItems(int i) { checkArgument(i >= 0); set("totalItems", i); return (B)this; } /** * Set the number of items per page (must be non-negative) * @param i int * @return B **/ public B itemsPerPage(int i) { checkArgument(i >= 0); set("itemsPerPage", i); return (B)this; } /** * Set the starting index (must be non-negative) * @param i int * @return B **/ public B startIndex(int i) { checkArgument(i >= 0); set("startIndex", i); return (B)this; } /** * Specify that the collection contains items updated after the specified time * @param dt DateTime * @return B **/ public B itemsAfter(DateTime dt) { return _dt("itemsAfter", dt); } /** * Specify that the collection contains items updated after right now * @return B */ public B itemsAfterNow() { return _dtNow("itemsAfter"); } /** * Specify that the collection contains items updated a specific duration after now * @param duration Duration * @return B */ public B itemsAfterFromNow(ReadableDuration duration) { return _dtFromNow("itemsAfter", duration); } /** * Specify that the collection contains items updated a specific period after now * @param period * @return B */ public B itemsAfterFromNow(ReadablePeriod period) { return _dtFromNow("itemsAfter", period); } /** * Specify that the collection contains items updated a specific duration after now * @param v long * @param unit TimeUnit * @return B **/ public B itemsAfterFromNow(long v, TimeUnit unit) { return _dtFromNow("itemsAfter", v, unit); } /** * Specify that the collection contains items updated before a specific time * @param dt DateTime * @return B **/ public B itemsBefore(DateTime dt) { return _dt("itemsBefore", dt); } /** * Specify that the collection contains items updated before now * @return B */ public B itemsBeforeNow() { return _dtNow("itemsBefore"); } /** * Specify that the collection contains items updated a specific duration * before now * @param duration Duration * @return B */ public B itemsBeforeFromNow(ReadableDuration duration) { return _dtFromNow("itemsBefore", duration); } /** * Specify that the collection contains items updated a specific period * before now * @param period * @return B */ public B itemsBeforeFromNow(ReadablePeriod period) { return _dtFromNow("itemsBefore", period); } /** * Method itemsBeforeFromNow. * @param v long * @param unit TimeUnit * @return B **/ public B itemsBeforeFromNow(long v, TimeUnit unit) { return _dtFromNow("itemsBefore", v, unit); } /** * Adds a paging link * <pre> * Collection collection = Makers.collection() * .pageLink(Page.NEXT, "http://example.org") * .get(); * </pre> * @param page * @param url * @return B */ public B pageLink(Page page, String url) { return link(page.label, linkValue(url)); } /** * Adds a paging link * @param page * @param link * @return B */ public B pageLink(Page page, LinkValue link) { return link(page.label, link); } /** * Adds a paging link * @param page * @param link * @return B */ public B pageLink(Page page, Supplier<? extends LinkValue> link) { return link(page.label, link); } } /** * Constructor for Collection. * @param builder Collection.AbstractBuilder<?,?> */ Collection(Collection.AbstractBuilder<?, ?> builder) { super(builder); } /** * Returns the total number of items * @return int **/ public int totalItems() { return getInt("totalItems"); } /** * If not null, indicates that the collection only contains items * updated after the given instant * @return DateTime **/ public DateTime itemsAfter() { return this.getDateTime("itemsAfter"); } /** * If not null, indicates that the collection only contains items * updated before the given instant * @return DateTime * */ public DateTime itemsBefore() { return this.getDateTime("itemsBefore"); } /** * Returns the number of items per page * @return int **/ public int itemsPerPage() { return this.getInt("itemsPerPage"); } /** * Returns the start index for this page * @return int **/ public int startIndex() { return this.getInt("startIndex"); } /** * Returns a listing of Paging links that * exist on this collection * @return Iterable<Page> */ public Iterable<Page> pages() { ImmutableSet.Builder<Page> pages = ImmutableSet.builder(); for (Page page : Page.values()) if (has(page.label)) pages.add(page); return pages.build(); } /** * Returns a listing of Paging LinkValues * @param page Page The type of paging link to return * @return Iterable<LinkValue> */ public Iterable<LinkValue> pageLink( Page page) { return page.links(this); } /** * Returns a listing of Paging LinkValues * @param page Page The type of paging link to return * @param filter Predicate<? super LinkValue> A filter * @return Iterable<LinkValue> */ public Iterable<LinkValue> pageLink( Page page, Predicate<? super LinkValue> filter) { return page.links(this,filter); } /** * Returns the first matching paging LinkValue * @param page Page The type of paging link to return * @return LinkValue */ public LinkValue firstPageLink( Page page) { return page.first(this); } /** * Returns the first matching paging LinkValue * @param page Page the type of paging link to return * @param test com.google.common.base.Predicate<? super LinkValue> a filter * @return LinkValue */ public LinkValue firstMatchingPageLink( Page page, Predicate<? super LinkValue> test) { return page.firstMatching(this,test); } /** * Returns the collection of items * @return java.util.Iterable<A> **/ public <A extends ASObject>Iterable<A> items() { return this.<Iterable<A>>get("items"); } /** * Returns a filtered collection of items * @param filter com.google.common.base.Predicate<? super A> filter * @return java.util.Iterable<A> */ public <A extends ASObject>Iterable<A> items( Predicate<? super A> filter) { return filter(this.<A>items(), filter); } // Java Serialization Support Object writeReplace() throws java.io.ObjectStreamException { return new SerializedForm(this); } private static class SerializedForm extends AbstractSerializedForm<Collection> { protected SerializedForm(Collection obj) { super(obj); } private static final long serialVersionUID = -1975376657749952999L; protected Collection.Builder builder() { return Makers.collection(); } Object readResolve() throws ObjectStreamException { return super.doReadResolve(); } } }