1/**
2 * $Revision$
3 * $Date$
4 *
5 * Copyright 2003-2007 Jive Software.
6 *
7 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20package org.jivesoftware.smackx.bookmark;
21
22import org.jivesoftware.smack.Connection;
23import org.jivesoftware.smack.XMPPException;
24import org.jivesoftware.smackx.PrivateDataManager;
25
26import java.util.*;
27
28/**
29 * Provides methods to manage bookmarks in accordance with JEP-0048. Methods for managing URLs and
30 * Conferences are provided.
31 * </p>
32 * It should be noted that some extensions have been made to the JEP. There is an attribute on URLs
33 * that marks a url as a news feed and also a sub-element can be added to either a URL or conference
34 * indicated that it is shared amongst all users on a server.
35 *
36 * @author Alexander Wenckus
37 */
38public class BookmarkManager {
39    private static final Map<Connection, BookmarkManager> bookmarkManagerMap = new HashMap<Connection, BookmarkManager>();
40    static {
41        PrivateDataManager.addPrivateDataProvider("storage", "storage:bookmarks",
42                new Bookmarks.Provider());
43    }
44
45    /**
46     * Returns the <i>BookmarkManager</i> for a connection, if it doesn't exist it is created.
47     *
48     * @param connection the connection for which the manager is desired.
49     * @return Returns the <i>BookmarkManager</i> for a connection, if it doesn't
50     * exist it is created.
51     * @throws XMPPException Thrown if the connection is null or has not yet been authenticated.
52     */
53    public synchronized static BookmarkManager getBookmarkManager(Connection connection)
54            throws XMPPException
55    {
56        BookmarkManager manager = (BookmarkManager) bookmarkManagerMap.get(connection);
57        if(manager == null) {
58            manager = new BookmarkManager(connection);
59            bookmarkManagerMap.put(connection, manager);
60        }
61        return manager;
62    }
63
64    private PrivateDataManager privateDataManager;
65    private Bookmarks bookmarks;
66    private final Object bookmarkLock = new Object();
67
68    /**
69     * Default constructor. Registers the data provider with the private data manager in the
70     * storage:bookmarks namespace.
71     *
72     * @param connection the connection for persisting and retrieving bookmarks.
73     * @throws XMPPException thrown when the connection is null or has not been authenticated.
74     */
75    private BookmarkManager(Connection connection) throws XMPPException {
76        if(connection == null || !connection.isAuthenticated()) {
77            throw new XMPPException("Invalid connection.");
78        }
79        this.privateDataManager = new PrivateDataManager(connection);
80    }
81
82    /**
83     * Returns all currently bookmarked conferences.
84     *
85     * @return returns all currently bookmarked conferences
86     * @throws XMPPException thrown when there was an error retrieving the current bookmarks from
87     * the server.
88     * @see BookmarkedConference
89     */
90    public Collection<BookmarkedConference> getBookmarkedConferences() throws XMPPException {
91        retrieveBookmarks();
92        return Collections.unmodifiableCollection(bookmarks.getBookmarkedConferences());
93    }
94
95    /**
96     * Adds or updates a conference in the bookmarks.
97     *
98     * @param name the name of the conference
99     * @param jid the jid of the conference
100     * @param isAutoJoin whether or not to join this conference automatically on login
101     * @param nickname the nickname to use for the user when joining the conference
102     * @param password the password to use for the user when joining the conference
103     * @throws XMPPException thrown when there is an issue retrieving the current bookmarks from
104     * the server.
105     */
106    public void addBookmarkedConference(String name, String jid, boolean isAutoJoin,
107            String nickname, String password) throws XMPPException
108    {
109        retrieveBookmarks();
110        BookmarkedConference bookmark
111                = new BookmarkedConference(name, jid, isAutoJoin, nickname, password);
112        List<BookmarkedConference> conferences = bookmarks.getBookmarkedConferences();
113        if(conferences.contains(bookmark)) {
114            BookmarkedConference oldConference = conferences.get(conferences.indexOf(bookmark));
115            if(oldConference.isShared()) {
116                throw new IllegalArgumentException("Cannot modify shared bookmark");
117            }
118            oldConference.setAutoJoin(isAutoJoin);
119            oldConference.setName(name);
120            oldConference.setNickname(nickname);
121            oldConference.setPassword(password);
122        }
123        else {
124            bookmarks.addBookmarkedConference(bookmark);
125        }
126        privateDataManager.setPrivateData(bookmarks);
127    }
128
129    /**
130     * Removes a conference from the bookmarks.
131     *
132     * @param jid the jid of the conference to be removed.
133     * @throws XMPPException thrown when there is a problem with the connection attempting to
134     * retrieve the bookmarks or persist the bookmarks.
135     * @throws IllegalArgumentException thrown when the conference being removed is a shared
136     * conference
137     */
138    public void removeBookmarkedConference(String jid) throws XMPPException {
139        retrieveBookmarks();
140        Iterator<BookmarkedConference> it = bookmarks.getBookmarkedConferences().iterator();
141        while(it.hasNext()) {
142            BookmarkedConference conference = it.next();
143            if(conference.getJid().equalsIgnoreCase(jid)) {
144                if(conference.isShared()) {
145                    throw new IllegalArgumentException("Conference is shared and can't be removed");
146                }
147                it.remove();
148                privateDataManager.setPrivateData(bookmarks);
149                return;
150            }
151        }
152    }
153
154    /**
155     * Returns an unmodifiable collection of all bookmarked urls.
156     *
157     * @return returns an unmodifiable collection of all bookmarked urls.
158     * @throws XMPPException thrown when there is a problem retriving bookmarks from the server.
159     */
160    public Collection<BookmarkedURL> getBookmarkedURLs() throws XMPPException {
161        retrieveBookmarks();
162        return Collections.unmodifiableCollection(bookmarks.getBookmarkedURLS());
163    }
164
165    /**
166     * Adds a new url or updates an already existing url in the bookmarks.
167     *
168     * @param URL the url of the bookmark
169     * @param name the name of the bookmark
170     * @param isRSS whether or not the url is an rss feed
171     * @throws XMPPException thrown when there is an error retriving or saving bookmarks from or to
172     * the server
173     */
174    public void addBookmarkedURL(String URL, String name, boolean isRSS) throws XMPPException {
175        retrieveBookmarks();
176        BookmarkedURL bookmark = new BookmarkedURL(URL, name, isRSS);
177        List<BookmarkedURL> urls = bookmarks.getBookmarkedURLS();
178        if(urls.contains(bookmark)) {
179            BookmarkedURL oldURL = urls.get(urls.indexOf(bookmark));
180            if(oldURL.isShared()) {
181                throw new IllegalArgumentException("Cannot modify shared bookmarks");
182            }
183            oldURL.setName(name);
184            oldURL.setRss(isRSS);
185        }
186        else {
187            bookmarks.addBookmarkedURL(bookmark);
188        }
189        privateDataManager.setPrivateData(bookmarks);
190    }
191
192    /**
193     *  Removes a url from the bookmarks.
194     *
195     * @param bookmarkURL the url of the bookmark to remove
196     * @throws XMPPException thrown if there is an error retriving or saving bookmarks from or to
197     * the server.
198     */
199    public void removeBookmarkedURL(String bookmarkURL) throws XMPPException {
200        retrieveBookmarks();
201        Iterator<BookmarkedURL> it = bookmarks.getBookmarkedURLS().iterator();
202        while(it.hasNext()) {
203            BookmarkedURL bookmark = it.next();
204            if(bookmark.getURL().equalsIgnoreCase(bookmarkURL)) {
205                if(bookmark.isShared()) {
206                    throw new IllegalArgumentException("Cannot delete a shared bookmark.");
207                }
208                it.remove();
209                privateDataManager.setPrivateData(bookmarks);
210                return;
211            }
212        }
213    }
214
215    private Bookmarks retrieveBookmarks() throws XMPPException {
216        synchronized(bookmarkLock) {
217            if(bookmarks == null) {
218                bookmarks = (Bookmarks) privateDataManager.getPrivateData("storage",
219                        "storage:bookmarks");
220            }
221            return bookmarks;
222        }
223    }
224}
225