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;
022import org.jivesoftware.smack.SmackException.NoResponseException;
023import org.jivesoftware.smack.SmackException.NotConnectedException;
024import org.jivesoftware.smack.XMPPConnection;
025import org.jivesoftware.smack.XMPPException.XMPPErrorException;
026import org.jivesoftware.smack.packet.IQ;
027import org.jivesoftware.smack.packet.SimpleIQ;
028import org.jivesoftware.smack.provider.IQProvider;
029import org.jivesoftware.smack.util.PacketParserUtils;
030import org.jivesoftware.smackx.xdata.Form;
031import org.jivesoftware.smackx.xdata.FormField;
032import org.jivesoftware.smackx.xdata.packet.DataForm;
033import org.xmlpull.v1.XmlPullParser;
034import org.xmlpull.v1.XmlPullParserException;
035
036/**
037 * Implements the protocol currently used to search information repositories on the Jabber network. To date, the jabber:iq:search protocol
038 * 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).
039 * However, the jabber:iq:search protocol is not limited to user directories, and could be used to search other Jabber information repositories
040 * (such as chatroom directories) or even to provide a Jabber interface to conventional search engines.
041 * <p/>
042 * The basic functionality is to query an information repository regarding the possible search fields, to send a search query, and to receive search results.
043 *
044 * @author Derek DeMoro
045 */
046public class UserSearch extends SimpleIQ {
047
048    public static final String ELEMENT = QUERY_ELEMENT;
049    public static final String NAMESPACE = "jabber:iq:search";
050
051    /**
052     * Creates a new instance of UserSearch.
053     */
054    public UserSearch() {
055        super(ELEMENT, NAMESPACE);
056    }
057
058    /**
059     * Returns the form for all search fields supported by the search service.
060     *
061     * @param con           the current XMPPConnection.
062     * @param searchService the search service to use. (ex. search.jivesoftware.com)
063     * @return the search form received by the server.
064     * @throws XMPPErrorException 
065     * @throws NoResponseException 
066     * @throws NotConnectedException 
067     */
068    public Form getSearchForm(XMPPConnection con, String searchService) throws NoResponseException, XMPPErrorException, NotConnectedException {
069        UserSearch search = new UserSearch();
070        search.setType(IQ.Type.get);
071        search.setTo(searchService);
072
073        IQ response = (IQ) con.createPacketCollectorAndSend(search).nextResultOrThrow();
074        return Form.getFormFrom(response);
075    }
076
077    /**
078     * Sends the filled out answer form to be sent and queried by the search service.
079     *
080     * @param con           the current XMPPConnection.
081     * @param searchForm    the <code>Form</code> to send for querying.
082     * @param searchService the search service to use. (ex. search.jivesoftware.com)
083     * @return ReportedData the data found from the query.
084     * @throws XMPPErrorException 
085     * @throws NoResponseException 
086     * @throws NotConnectedException 
087     */
088    public ReportedData sendSearchForm(XMPPConnection con, Form searchForm, String searchService) throws NoResponseException, XMPPErrorException, NotConnectedException {
089        UserSearch search = new UserSearch();
090        search.setType(IQ.Type.set);
091        search.setTo(searchService);
092        search.addExtension(searchForm.getDataFormToSend());
093
094        IQ response = (IQ) con.createPacketCollectorAndSend(search).nextResultOrThrow();
095        return ReportedData.getReportedDataFrom(response);
096    }
097
098    /**
099     * Sends the filled out answer form to be sent and queried by the search service.
100     *
101     * @param con           the current XMPPConnection.
102     * @param searchForm    the <code>Form</code> to send for querying.
103     * @param searchService the search service to use. (ex. search.jivesoftware.com)
104     * @return ReportedData the data found from the query.
105     * @throws XMPPErrorException 
106     * @throws NoResponseException 
107     * @throws NotConnectedException 
108     */
109    public ReportedData sendSimpleSearchForm(XMPPConnection con, Form searchForm, String searchService) throws NoResponseException, XMPPErrorException, NotConnectedException {
110        SimpleUserSearch search = new SimpleUserSearch();
111        search.setForm(searchForm);
112        search.setType(IQ.Type.set);
113        search.setTo(searchService);
114
115        SimpleUserSearch response = (SimpleUserSearch) con.createPacketCollectorAndSend(search).nextResultOrThrow();
116        return response.getReportedData();
117    }
118
119    /**
120     * Internal Search service Provider.
121     */
122    public static class Provider extends IQProvider<IQ> {
123
124        // FIXME this provider does return two different types of IQs
125        @Override
126        public IQ parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException {
127            UserSearch search = null;
128            SimpleUserSearch simpleUserSearch = new SimpleUserSearch();
129
130            boolean done = false;
131            while (!done) {
132                int eventType = parser.next();
133                if (eventType == XmlPullParser.START_TAG && parser.getName().equals("instructions")) {
134                    buildDataForm(simpleUserSearch, parser.nextText(), parser);
135                    return simpleUserSearch;
136                }
137                else if (eventType == XmlPullParser.START_TAG && parser.getName().equals("item")) {
138                    simpleUserSearch.parseItems(parser);
139                    return simpleUserSearch;
140                }
141                else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) {
142                    // Otherwise, it must be a packet extension.
143                    search = new UserSearch();
144                    PacketParserUtils.addExtensionElement(search, parser);
145                }
146                else if (eventType == XmlPullParser.END_TAG) {
147                    if (parser.getName().equals("query")) {
148                        done = true;
149                    }
150                }
151            }
152
153            if (search != null) {
154                return search;
155            }
156            return simpleUserSearch;
157        }
158    }
159
160    private static void buildDataForm(SimpleUserSearch search,
161                    String instructions, XmlPullParser parser)
162                    throws XmlPullParserException, IOException, SmackException {
163        DataForm dataForm = new DataForm(DataForm.Type.form);
164        boolean done = false;
165        dataForm.setTitle("User Search");
166        dataForm.addInstruction(instructions);
167        while (!done) {
168            int eventType = parser.next();
169
170            if (eventType == XmlPullParser.START_TAG && !parser.getNamespace().equals("jabber:x:data")) {
171                String name = parser.getName();
172                FormField field = new FormField(name);
173
174                // Handle hard coded values.
175                if(name.equals("first")){
176                    field.setLabel("First Name");
177                }
178                else if(name.equals("last")){
179                    field.setLabel("Last Name");
180                }
181                else if(name.equals("email")){
182                    field.setLabel("Email Address");
183                }
184                else if(name.equals("nick")){
185                    field.setLabel("Nickname");
186                }
187
188                field.setType(FormField.Type.text_single);
189                dataForm.addField(field);
190            }
191            else if (eventType == XmlPullParser.END_TAG) {
192                if (parser.getName().equals("query")) {
193                    done = true;
194                }
195            }
196            else if (eventType == XmlPullParser.START_TAG && parser.getNamespace().equals("jabber:x:data")) {
197                PacketParserUtils.addExtensionElement(search, parser);
198                done = true;
199            }
200        }
201        if (search.getExtension("x", "jabber:x:data") == null) {
202            search.addExtension(dataForm);
203        }
204    }
205
206
207}