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