/*******************************************************************************
* Copyright (c) 2008 Marcelo Mayworm. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: Marcelo Mayworm - initial API and implementation
*
******************************************************************************/
package org.eclipse.ecf.internal.provider.xmpp.search;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.identity.Namespace;
import org.eclipse.ecf.core.user.IUser;
import org.eclipse.ecf.core.user.User;
import org.eclipse.ecf.core.util.ECFException;
import org.eclipse.ecf.internal.provider.xmpp.Messages;
import org.eclipse.ecf.internal.provider.xmpp.XmppPlugin;
import org.eclipse.ecf.internal.provider.xmpp.smack.ECFConnection;
import org.eclipse.ecf.presence.search.AbstractUserSearchManager;
import org.eclipse.ecf.presence.search.ICriteria;
import org.eclipse.ecf.presence.search.ICriterion;
import org.eclipse.ecf.presence.search.IRestriction;
import org.eclipse.ecf.presence.search.IResultList;
import org.eclipse.ecf.presence.search.ISearch;
import org.eclipse.ecf.presence.search.IUserSearchEvent;
import org.eclipse.ecf.presence.search.IUserSearchListener;
import org.eclipse.ecf.presence.search.IUserSearchManager;
import org.eclipse.ecf.presence.search.ResultList;
import org.eclipse.ecf.presence.search.UserSearchCompleteEvent;
import org.eclipse.ecf.presence.search.UserSearchException;
import org.eclipse.ecf.provider.xmpp.identity.XMPPID;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.FormField;
import org.jivesoftware.smackx.ReportedData;
import org.jivesoftware.smackx.ReportedData.Row;
import org.jivesoftware.smackx.search.UserSearch;
/**
* A specific implementation for XMPP provider. XEP-0055:
* http://www.xmpp.org/extensions/xep-0055.html
*
* @see IUserSearchManager
* @since 3.0
*/
public class XMPPUserSearchManager extends AbstractUserSearchManager {
/** Search service name on XMPP server */
protected static final String SERVICE_SEARCH = "search."; //$NON-NLS-1$
/** Wrapper for XMPP connection */
protected ECFConnection ecfConnection;
protected Namespace connectNamespace;
protected ID connectedID;
protected Form form;
protected UserSearch manager;
protected boolean enabled;
protected ICriteria criteria;
protected static final String FORM_TYPE = "FORM_TYPE"; //$NON-NLS-1$
protected static final String NAME = "name"; //$NON-NLS-1$
protected static final String JID = "jid"; //$NON-NLS-1$
protected static final String SEARCH_ACTION = "search"; //$NON-NLS-1$
public XMPPUserSearchManager() {
manager = new UserSearch();
}
/**
* Create a specific {@link ICriteria} for XMPP
*/
public ICriteria createCriteria() {
return new XMPPCriteria();
}
/**
* Create a specific {@link IRestriction} for XMPP
*/
public IRestriction createRestriction() {
return new XMPPSelection();
}
/**
* Specific implementation for XMPP
*
* @see IUserSearchManager#search(ICriteria).
*/
public ISearch search(ICriteria criteria) throws UserSearchException {
ResultList resultList = new ResultList();
try {
// initialize the form by chance it is null
if (form == null)
form = manager.getSearchForm(ecfConnection.getXMPPConnection(),
SERVICE_SEARCH
+ ecfConnection.getXMPPConnection()
.getServiceName());
/*
* For XMPP criterion is considered. The XMPP server will deal with
* the search.
*/
List criterions = criteria.getCriterions();
// add the fields for the search dynamically
// consider just the fields used on the search
// fields checked by user
String fields[] = getUserPropertiesFields();
for (int i = 0; i < fields.length; i++) {
Iterator criterionsIterator = criterions.iterator();
// for each user properties field check if it
// was added by user for the criteria
// for each field, a search is performed, and
// the partial result is added to the result list
while (criterionsIterator.hasNext()) {
ICriterion criterion = (ICriterion) criterionsIterator
.next();
if (criterion.equals(fields[i])) {
Form answerForm = form.createAnswerForm();
answerForm.setAnswer(fields[i], true);
answerForm.setAnswer(SEARCH_ACTION, criterion
.toExpression());
ReportedData data = manager.sendSearchForm(
ecfConnection.getXMPPConnection(), answerForm,
SERVICE_SEARCH
+ ecfConnection.getXMPPConnection()
.getServiceName());
// create a result list from ReportedData
IResultList partialResultList = createResultList(data);
resultList.addAll(partialResultList.getResults());
}
}
}
return new XMPPSearch(resultList);
} catch (final XMPPException e) {
String message = null;
if (e.getXMPPError() != null && e.getXMPPError().getCode() == 404) {
message = Messages.XMPPContainer_UNRECOGONIZED_SEARCH_SERVICE;
} else {
message = e.getLocalizedMessage();
}
throw new UserSearchException(message, e, criteria);
} catch (ECFException e) {
throw new UserSearchException(e, criteria);
}
}
/**
* Create a result list from ReportedData. Identify dynamically columns and
* rows and create users adding it to a {@link IResultList}
*
* @param data
* ReportedData
* @return {@link IResultList} a list of users
* @throws
*/
protected IResultList createResultList(ReportedData data) {
ResultList result = new ResultList();
Iterator rows = data.getRows();
while (rows.hasNext()) {
Row row = (Row) rows.next();
Iterator jids = row.getValues(JID);
Iterator names = row.getValues(NAME);
String jid = null;
String name = null;
// XMPP server returns the same length for both
while (jids.hasNext() && names.hasNext()) {
try {
jid = (String) jids.next();
name = (String) names.next();
IUser user = new User(new XMPPID(connectNamespace, jid),
name);
result.add(new XMPPResultItem(user));
} catch (URISyntaxException e) {
throw new RuntimeException(
"cannot create connect id for client " + jid //$NON-NLS-1$
+ " , name = " + name, e); //$NON-NLS-1$
}
}
}
return result;
}
/**
* Specific implementation for XMPP
*
* @see IUserSearchManager#search(ICriteria, IUserSearchListener)
*/
public void search(final ICriteria criteria, final IUserSearchListener listener) {
Assert.isNotNull(criteria);
Assert.isNotNull(listener);
Job job = new Job(Messages.XMPPUserSearchManager_JOB) {
protected IStatus run(IProgressMonitor monitor) {
try {
ISearch search = search(criteria);
UserSearchCompleteEvent complete = new UserSearchCompleteEvent(
search);
listener.handleUserSearchEvent(complete);
} catch (UserSearchException e) {
log(e.getLocalizedMessage(), e);
return new Status(IStatus.ERROR,XmppPlugin.PLUGIN_ID,IStatus.ERROR,"Exception in user search",e);
}
return Status.OK_STATUS;
}
};
job.schedule();
}
/**
* These parameters must be not null
*
* @param connectNamespace
* @param connectedID
* @param connection
*/
public void setConnection(Namespace connectNamespace, ID connectedID,
ECFConnection connection) {
Assert.isNotNull(connectNamespace);
Assert.isNotNull(connectedID);
Assert.isNotNull(connection);
this.connectNamespace = connectNamespace;
this.connectedID = connectedID;
this.ecfConnection = connection;
}
/**
* Returns the user properties fields available on the XMPP server
*
* @param form
* @return String[] fields for form
* @throws ECFException
*/
public String[] getUserPropertiesFields() throws ECFException {
try {
if (form == null)
form = manager.getSearchForm(ecfConnection.getXMPPConnection(),
SERVICE_SEARCH
+ ecfConnection.getXMPPConnection()
.getServiceName());
Set fields = new HashSet();
Iterator userProperties = form.getFields();
while (userProperties.hasNext()) {
FormField field = (FormField) userProperties.next();
String variable = field.getVariable();
// ignore these fields
if (!variable.equalsIgnoreCase(FORM_TYPE)
&& !variable.equalsIgnoreCase(SEARCH_ACTION))
fields.add(variable);
}
return (String[]) fields.toArray(new String[0]);
} catch (final XMPPException e) {
String message = null;
if (e.getXMPPError() != null && e.getXMPPError().getCode() == 404) {
message = Messages.XMPPContainer_UNRECOGONIZED_SEARCH_SERVICE;
} else {
message = e.getLocalizedMessage();
}
throw new ECFException(message, e);
}
}
/**
* Notify that user search for XMPP is enabled
*/
public boolean isEnabled() {
return this.enabled;
}
/**
* For gtalk it should be not enabled
*
* @param enabled
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
// utility methods
protected void log(String msg, Throwable e) {
XmppPlugin.log(msg, e);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ecf.presence.search.AbstractUserSearchManager#fireUserSearchEvent
* (org.eclipse.ecf.presence.search.IUserSearchEvent)
*/
public void fireUserSearchEvent(IUserSearchEvent event) {
super.fireUserSearchEvent(event);
}
}