001/**
002 *
003 * Copyright 2003-2007 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.search;
018
019import java.io.IOException;
020
021import org.jivesoftware.smack.SmackException.NoResponseException;
022import org.jivesoftware.smack.SmackException.NotConnectedException;
023import org.jivesoftware.smack.XMPPConnection;
024import org.jivesoftware.smack.XMPPException.XMPPErrorException;
025import org.jivesoftware.smack.packet.IQ;
026import org.jivesoftware.smack.packet.SimpleIQ;
027import org.jivesoftware.smack.packet.XmlEnvironment;
028import org.jivesoftware.smack.parsing.SmackParsingException;
029import org.jivesoftware.smack.provider.IQProvider;
030import org.jivesoftware.smack.util.PacketParserUtils;
031import org.jivesoftware.smack.xml.XmlPullParser;
032import org.jivesoftware.smack.xml.XmlPullParserException;
033
034import org.jivesoftware.smackx.xdata.packet.DataForm;
035
036import org.jxmpp.jid.DomainBareJid;
037
038/**
039 * Implements the protocol currently used to search information repositories on the Jabber network. To date, the jabber:iq:search protocol
040 * has been used mainly to search for people who have registered with user directories (e.g., the "Jabber User Directory" hosted at users.jabber.org).
041 * However, the jabber:iq:search protocol is not limited to user directories, and could be used to search other Jabber information repositories
042 * (such as chatroom directories) or even to provide a Jabber interface to conventional search engines.
043 *
044 * The basic functionality is to query an information repository regarding the possible search fields, to send a search query, and to receive search results.
045 *
046 * @author Derek DeMoro
047 */
048public class UserSearch extends SimpleIQ {
049
050    public static final String ELEMENT = QUERY_ELEMENT;
051    public static final String NAMESPACE = "jabber:iq:search";
052
053    /**
054     * Creates a new instance of UserSearch.
055     */
056    public UserSearch() {
057        super(ELEMENT, NAMESPACE);
058    }
059
060    /**
061     * Returns the form for all search fields supported by the search service.
062     *
063     * @param con           the current XMPPConnection.
064     * @param searchService the search service to use. (ex. search.jivesoftware.com)
065     * @return the search form received by the server.
066     * @throws XMPPErrorException if there was an XMPP error returned.
067     * @throws NoResponseException if there was no response from the remote entity.
068     * @throws NotConnectedException if the XMPP connection is not connected.
069     * @throws InterruptedException if the calling thread was interrupted.
070     */
071    public DataForm getSearchForm(XMPPConnection con, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
072        UserSearch search = new UserSearch();
073        search.setType(IQ.Type.get);
074        search.setTo(searchService);
075
076        IQ response = con.createStanzaCollectorAndSend(search).nextResultOrThrow();
077        return DataForm.from(response, NAMESPACE);
078    }
079
080    /**
081     * Sends the filled out answer form to be sent and queried by the search service.
082     *
083     * @param con           the current XMPPConnection.
084     * @param searchForm    the <code>Form</code> to send for querying.
085     * @param searchService the search service to use. (ex. search.jivesoftware.com)
086     * @return ReportedData the data found from the query.
087     * @throws XMPPErrorException if there was an XMPP error returned.
088     * @throws NoResponseException if there was no response from the remote entity.
089     * @throws NotConnectedException if the XMPP connection is not connected.
090     * @throws InterruptedException if the calling thread was interrupted.
091     */
092    public ReportedData sendSearchForm(XMPPConnection con, DataForm searchForm, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
093        UserSearch search = new UserSearch();
094        search.setType(IQ.Type.set);
095        search.setTo(searchService);
096        search.addExtension(searchForm);
097
098        IQ response = con.createStanzaCollectorAndSend(search).nextResultOrThrow();
099        return ReportedData.getReportedDataFrom(response);
100    }
101
102    /**
103     * Sends the filled out answer form to be sent and queried by the search service.
104     *
105     * @param con           the current XMPPConnection.
106     * @param searchForm    the <code>Form</code> to send for querying.
107     * @param searchService the search service to use. (ex. search.jivesoftware.com)
108     * @return ReportedData the data found from the query.
109     * @throws XMPPErrorException if there was an XMPP error returned.
110     * @throws NoResponseException if there was no response from the remote entity.
111     * @throws NotConnectedException if the XMPP connection is not connected.
112     * @throws InterruptedException if the calling thread was interrupted.
113     */
114    public ReportedData sendSimpleSearchForm(XMPPConnection con, DataForm searchForm, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
115        SimpleUserSearch search = new SimpleUserSearch();
116        search.setForm(searchForm);
117        search.setType(IQ.Type.set);
118        search.setTo(searchService);
119
120        SimpleUserSearch response = con.createStanzaCollectorAndSend(search).nextResultOrThrow();
121        return response.getReportedData();
122    }
123
124    /**
125     * Internal Search service Provider.
126     */
127    public static class Provider extends IQProvider<IQ> {
128
129        // FIXME this provider does return two different types of IQs
130        @Override
131        public IQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
132            UserSearch search = null;
133            SimpleUserSearch simpleUserSearch = new SimpleUserSearch();
134
135            boolean done = false;
136            while (!done) {
137                XmlPullParser.Event eventType = parser.next();
138                if (eventType == XmlPullParser.Event.START_ELEMENT && parser.getName().equals("item")) {
139                    simpleUserSearch.parseItems(parser);
140                    return simpleUserSearch;
141                }
142                else if (eventType == XmlPullParser.Event.START_ELEMENT && parser.getNamespace().equals("jabber:x:data")) {
143                    // Otherwise, it must be a packet extension.
144                    search = new UserSearch();
145                    PacketParserUtils.addExtensionElement(search, parser, xmlEnvironment);
146                }
147                else if (eventType == XmlPullParser.Event.END_ELEMENT) {
148                    if (parser.getName().equals("query")) {
149                        done = true;
150                    }
151                }
152            }
153
154            if (search != null) {
155                return search;
156            }
157            return simpleUserSearch;
158        }
159    }
160
161}