Java tutorial
/** * Copyright (C) 2008 Guenther Niess. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.frogx.service.core.iq; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.QName; import org.frogx.service.api.MUGManager; import org.frogx.service.api.MUGRoom; import org.frogx.service.api.MultiUserGame; import org.frogx.service.api.util.LocaleUtil; import org.frogx.service.core.DefaultMUGService; import org.xmpp.forms.DataForm; import org.xmpp.forms.FormField; import org.xmpp.packet.IQ; import org.xmpp.packet.PacketError; import org.xmpp.packet.PacketError.Condition; import org.xmpp.resultsetmanagement.ResultSet; import org.xmpp.resultsetmanagement.ResultSetImpl; /** * This class is implementing the Jabber Search (<a href="http://xmpp.org/extensions/xep-0055.html">XEP-0055</a>) to search for rooms on a Multi-User Gaming service. * @author Günther Nieß */ public class IQSearchHandler { private static final String nameField = "mug#roomsearch_name"; private static final String exactNameField = "name_is_exact_match"; private static final String savedRoomsField = "mug#roomsearch_saved"; private static final String categoryField = "mug#roomsearch_category"; private static final String gameField = "mug#roomsearch_game"; private static final String rolesField = "mug#roomsearch_roles"; private static final String occupantsField = "mug#roomsearch_max_occupants"; /** * This service hosts our game rooms. * @uml.property name="service" * @uml.associationEnd */ DefaultMUGService service; /** * The MUGManager provides sending and routing packages. * @uml.property name="mugManager" * @uml.associationEnd */ private MUGManager mugManager; /** * A utility which provices localized strings. * @uml.property name="locale" * @uml.associationEnd */ private LocaleUtil locale; /** * Create a handler for searching rooms on the multi-user gaming service. * * @param service The multi-user service which offers the searched rooms. */ public IQSearchHandler(DefaultMUGService service, MUGManager mugManager) { this.service = service; this.mugManager = mugManager; this.locale = this.mugManager.getLocaleUtil(); } /** * Handle a Jabber Search query and get the resulting IQ packet. * * @param packet The IQ Query. * @return The IQ reply resulting from the query. */ public IQ handleIQ(IQ packet) { // Ignore packets with type error or result if (packet.getType() == IQ.Type.error || packet.getType() == IQ.Type.result) { return null; } IQ reply = IQ.createResultIQ(packet); Element query = packet.getChildElement(); Element formElement = query.element(QName.get("x", "jabber:x:data")); // check if its a jabber search request if (!query.getName().equals("query") || !query.getNamespaceURI().equals("jabber:iq:search")) { reply.setError(PacketError.Condition.bad_request); return reply; } if (formElement == null) { // return an empty form reply.setChildElement(getSearchForm()); } else { // get the list of the found rooms List<MUGRoom> mugrsm = processSearchForm(formElement); // handle jabber resulting set management Element set = query.element(QName.get("set", ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT)); ResultSet<MUGRoom> resultSet = new ResultSetImpl<MUGRoom>(sortByUserAmount(mugrsm)); if (set != null) { if (!ResultSet.isValidRSMRequest(set)) { reply.setError(Condition.bad_request); return reply; } try { mugrsm = resultSet.applyRSMDirectives(set); } catch (NullPointerException e) { reply.setError(Condition.item_not_found); return reply; } } Element resultQuery = reply.setChildElement("query", "jabber:iq:search"); DataForm resultForm = createResultingForm(mugrsm); if (resultForm != null) { resultQuery.add(resultForm.getElement()); if (set != null) resultQuery.add(resultSet.generateSetElementFromResults(mugrsm)); } } return reply; } /** * Parsing the Jabber Search form and looking for the searched rooms. * * @param formElement A xml element which describes the search form. * @return A list of found game rooms. */ private List<MUGRoom> processSearchForm(Element formElement) { // default search options String name = null; boolean exactName = false; boolean savedRooms = false; String category = null; FormField games = null; int minFreeRoles = 0; int maxOccupants = -1; // parse params from request. DataForm df = new DataForm(formElement); Iterator<String> values; String booleanValue; FormField field; field = df.getField(nameField); if (field != null) { values = field.getValues().iterator(); name = (values.hasNext() ? values.next() : null); if (name != null && name.trim().length() == 0) name = null; } field = df.getField(exactNameField); if (field != null) { values = field.getValues().iterator(); booleanValue = (values.hasNext() ? values.next() : "1"); exactName = "1".equals(booleanValue) || "true".equals(booleanValue); } field = df.getField(savedRoomsField); if (field != null) { values = field.getValues().iterator(); booleanValue = (values.hasNext() ? values.next() : "1"); savedRooms = "1".equals(booleanValue) || "true".equals(booleanValue); } field = df.getField(categoryField); if (field != null) { values = field.getValues().iterator(); category = (values.hasNext() ? values.next() : null); if ((category != null) && (category.equals("any") || category.trim().length() == 0)) category = null; } games = df.getField(gameField); field = df.getField(rolesField); if (field != null) { values = field.getValues().iterator(); minFreeRoles = (values.hasNext() ? Integer.valueOf(values.next()) : 0); } field = df.getField(occupantsField); if (field != null) { values = field.getValues().iterator(); maxOccupants = (values.hasNext() ? Integer.valueOf(values.next()) : -1); } Collection<MUGRoom> rooms = null; // TODO: Support saved rooms if (savedRooms) rooms = new ArrayList<MUGRoom>(); if (rooms == null && category != null) { rooms = service.getGameRoomsByCategory(category); category = null; } if (rooms == null) rooms = service.getGameRooms(); List<MUGRoom> searchResults; searchResults = new ArrayList<MUGRoom>(); for (MUGRoom room : rooms) { boolean find = true; if (name != null) { if (exactName) { if (!name.equalsIgnoreCase(room.getNaturalLanguageName())) { find = false; } } else { if (room.getNaturalLanguageName().toLowerCase().indexOf(name.toLowerCase()) == -1) { find = false; } } } if (category != null && !room.getGame().getCategory().equals(category)) { find = false; } if (games != null && games.getValues().iterator().hasNext()) { boolean withinCategory = false; for (Iterator<String> gameIterator = games.getValues().iterator(); gameIterator.hasNext();) { if (room.getGame().getNamespace().equals(gameIterator.next())) { withinCategory = true; break; } } if (!withinCategory) find = false; } if (minFreeRoles > 0 && (room.getMatch().getFreeRoles() == null || room.getMatch().getFreeRoles().size() < minFreeRoles)) { find = false; } if (maxOccupants != -1 && room.getMaxOccupants() < maxOccupants) { find = false; } if (find && room.isPublicRoom() && !room.isLocked()) searchResults.add(room); } return searchResults; } /** * Sorts the provided list in such a way that the MUG with the fewest users * will be the first one in the list. * * @param mugs The unordered list that will be sorted. * @return The sorted list of MUG rooms. */ private static List<MUGRoom> sortByUserAmount(List<MUGRoom> mugs) { Collections.sort(mugs, new Comparator<MUGRoom>() { public int compare(MUGRoom o1, MUGRoom o2) { return o2.getOccupantsCount() - o1.getOccupantsCount(); } }); return mugs; } /** * Creates a XMPP form with search results. * * @param rooms A collection of game rooms which should be listed in the result. * @return The Jabber form with the results. */ private DataForm createResultingForm(Collection<MUGRoom> rooms) { if (rooms == null || rooms.isEmpty()) return null; DataForm resultForm = new DataForm(DataForm.Type.result); FormField field = resultForm.addField(); field.setVariable("FORM_TYPE"); field.setType(FormField.Type.hidden); field.addValue("jabber:iq:search"); boolean atLeastoneResult = false; for (MUGRoom room : rooms) { Map<String, Object> fields = new HashMap<String, Object>(); fields.put("name", room.getNaturalLanguageName()); fields.put("game", room.getGame().getNamespace()); fields.put("jid", room.getJID().toString()); resultForm.addItemFields(fields); atLeastoneResult = true; } if (atLeastoneResult) { resultForm.addReportedField("name", locale.getLocalizedString("mug.search.result.name"), FormField.Type.text_single); resultForm.addReportedField("game", locale.getLocalizedString("mug.search.result.game"), FormField.Type.text_single); resultForm.addReportedField("jid", locale.getLocalizedString("mug.search.result.jid"), FormField.Type.jid_single); } return resultForm; } /** * Creates a Jabber Search form and returns the XML query element. * * @return A search element with an empty form. */ private Element getSearchForm() { Element element = DocumentHelper.createElement(QName.get("query", "jabber:iq:search")); DataForm searchForm = new DataForm(DataForm.Type.form); Map<String, MultiUserGame> games = service.getSupportedGames(); searchForm.setTitle(locale.getLocalizedString("mug.search.form.title")); searchForm.addInstruction(locale.getLocalizedString("mug.search.form.instruction")); FormField field = searchForm.addField(); field.setVariable("FORM_TYPE"); field.setType(FormField.Type.hidden); field.addValue("jabber:iq:search"); field = searchForm.addField(); field.setVariable(nameField); field.setType(FormField.Type.text_single); field.setLabel(locale.getLocalizedString("mug.search.form.name")); field.setRequired(false); field = searchForm.addField(); field.setVariable(exactNameField); field.setType(FormField.Type.boolean_type); field.setLabel(locale.getLocalizedString("mug.search.form.exact_name")); field.setRequired(false); field = searchForm.addField(); field.setVariable(savedRoomsField); field.setType(FormField.Type.boolean_type); field.setLabel(locale.getLocalizedString("mug.search.form.saved")); field.setRequired(false); field = searchForm.addField(); field.setVariable(rolesField); field.setType(FormField.Type.list_single); field.setLabel(locale.getLocalizedString("mug.search.form.min_roles")); field.setRequired(false); for (int i = 0; i <= 15; i++) { field.addOption(Integer.toString(i), Integer.toString(i)); } field.addValue("0"); field = searchForm.addField(); field.setVariable(occupantsField); field.setType(FormField.Type.list_single); field.setLabel(locale.getLocalizedString("mug.search.form.max_occupants")); field.setRequired(false); for (int i = 5; i <= 40; i = i + 5) { field.addOption(Integer.toString(i), Integer.toString(i)); } field.addValue("20"); if (games != null && !games.isEmpty()) { Collection<String> categories = service.getGameCategories(); field = searchForm.addField(); field.setVariable(categoryField); field.setType(FormField.Type.list_single); field.setLabel(locale.getLocalizedString("mug.search.form.category")); field.setRequired(false); if (categories.size() > 1) { field.addOption("any", "any"); field.addValue("any"); } for (String category : categories) { field.addOption(category, category); } field = searchForm.addField(); field.setVariable(gameField); field.setType(FormField.Type.list_multi); field.setLabel(locale.getLocalizedString("mug.search.form.games")); field.setRequired(false); for (MultiUserGame game : games.values()) { field.addOption(game.getDescription(), game.getNamespace()); } } element.add(searchForm.getElement()); return element; } }