package saros.stf.server.rmi.superbot.component.view.saros.impl; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.allOf; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.widgetOfType; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withRegex; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import org.apache.log4j.Logger; import org.eclipse.jface.bindings.keys.KeyStroke; import org.eclipse.jface.bindings.keys.ParseException; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Widget; import org.eclipse.swtbot.eclipse.finder.matchers.WidgetMatcherFactory; import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView; import org.eclipse.swtbot.swt.finder.SWTBot; import org.eclipse.swtbot.swt.finder.waits.Conditions; import org.eclipse.swtbot.swt.finder.waits.DefaultCondition; import org.eclipse.swtbot.swt.finder.widgets.SWTBotCTabItem; import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; import org.eclipse.swtbot.swt.finder.widgets.SWTBotToolbarButton; import org.eclipse.swtbot.swt.finder.widgets.SWTBotToolbarDropDownButton; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem; import org.hamcrest.Matcher; import org.jivesoftware.smack.Roster; import saros.editor.FollowModeManager; import saros.net.xmpp.JID; import saros.session.User; import saros.stf.server.StfRemoteObject; import saros.stf.server.bot.SarosSWTBotPreferences; import saros.stf.server.rmi.controlbot.impl.ControlBotImpl; import saros.stf.server.rmi.remotebot.impl.RemoteWorkbenchBot; import saros.stf.server.rmi.remotebot.widget.IRemoteBotView; import saros.stf.server.rmi.superbot.ISuperBot; import saros.stf.server.rmi.superbot.component.contextmenu.sarosview.IContextMenusInContactListArea; import saros.stf.server.rmi.superbot.component.contextmenu.sarosview.IContextMenusInSessionArea; import saros.stf.server.rmi.superbot.component.contextmenu.sarosview.impl.ContextMenusInContactListArea; import saros.stf.server.rmi.superbot.component.contextmenu.sarosview.impl.ContextMenusInSessionArea; import saros.stf.server.rmi.superbot.component.view.saros.IChatroom; import saros.stf.server.rmi.superbot.component.view.saros.ISarosView; import saros.stf.server.rmi.superbot.impl.SuperBot; import saros.stf.server.util.WidgetUtil; /** * This implementation of {@link ISarosView} * * @author lchen */ public final class SarosView extends StfRemoteObject implements ISarosView { private static final Logger log = Logger.getLogger(SarosView.class); private static final SarosView INSTANCE = new SarosView(); private SWTBotView view; private SWTBotTree tree; public static SarosView getInstance() { return INSTANCE; } public ISarosView setView(SWTBotView view) { setViewWithTree(view); return this; } @Override public void connectWith(JID jid, String password, boolean forceReconnect) throws RemoteException { ControlBotImpl.getInstance() .getAccountManipulator() .addAccount(jid.getName(), password, jid.getDomain()); boolean activated = ControlBotImpl.getInstance() .getAccountManipulator() .activateAccount(jid.getName(), jid.getDomain()); // already connected with the given account if (!forceReconnect && isConnected() && !activated) return; if (isConnected()) { clickToolbarButtonWithTooltip(TB_DISCONNECT); waitUntilIsDisconnected(); } clickToolbarButtonWithTooltip(TB_CONNECT); waitUntilIsConnected(); } @Override public void connect() throws RemoteException { if (isConnected()) disconnect(); if (getXmppAccountStore().isEmpty()) throw new RuntimeException("unable to connect with the active account, it does not exists"); clickToolbarButtonWithTooltip(TB_CONNECT); waitUntilIsConnected(); } @Override public void disconnect() throws RemoteException { if (isConnected()) { clickToolbarButtonWithTooltip(TB_DISCONNECT); waitUntilIsDisconnected(); } } /* * FIXME: there are some problems by clicking the toolbarDropDownButton. */ @SuppressWarnings("unused") private void selectConnectAccount(String baseJID) throws RemoteException { SWTBotToolbarDropDownButton b = view.toolbarDropDownButton(TB_CONNECT); @SuppressWarnings("static-access") Matcher<MenuItem> withRegex = WidgetMatcherFactory.withRegex(Pattern.quote(baseJID) + ".*"); b.menuItem(withRegex).click(); try { b.pressShortcut(KeyStroke.getInstance("ESC")); } catch (ParseException e) { log.debug("", e); } } @Override public void addContact(JID jid) throws RemoteException { if (!isInContactList(jid)) { clickToolbarButtonWithTooltip(TB_ADD_NEW_CONTACT); SuperBot.getInstance().confirmShellAddContact(jid); } // wait for update of the saros session tree new SWTBot().sleep(500); } @Override public void sendFileToUser(JID jid) throws RemoteException { selectParticipant(jid, "you cannot send a file to youself"); clickToolbarButtonWithTooltip(TB_SEND_A_FILE_TO_SELECTED_CONTACT); } @Override public void leaveSession() throws RemoteException { if (isInSession()) { if (!isHost()) { clickToolbarButtonWithTooltip(TB_LEAVE_SESSION); SWTBotShell shell = new SWTBot().shell(SHELL_CONFIRM_LEAVING_SESSION); shell.activate(); shell.bot().button(YES).click(); } else { boolean isLastInSession = tree.getTreeItem(NODE_SESSION).getNodes().size() == 1; clickToolbarButtonWithTooltip(TB_STOP_SESSION); if (!isLastInSession) { SWTBotShell shell = new SWTBot().shell(SHELL_CONFIRM_CLOSING_SESSION); shell.activate(); shell.bot().button(YES).click(); } } waitUntilIsNotInSession(); } } /** * Note: {@link StfRemoteObject#TB_INCONSISTENCY_DETECTED} is not complete toolbarName, so we need * to use {@link IRemoteBotView#toolbarButtonWithRegex(String)} to perform this action. */ @Override public void resolveInconsistency() throws RemoteException { WidgetUtil.getToolbarButtonWithRegex(view, Pattern.quote(TB_INCONSISTENCY_DETECTED) + ".*") .click(); SWTBot bot; bot = new SWTBot(); bot.shell(SHELL_CONFIRM_CONSISTENCY_RECOVERY).bot().button(OK).click(); bot = new SWTBot(); bot.sleep(1000); bot.waitWhile( Conditions.shellIsActive(SHELL_PROGRESS_INFORMATION), SarosSWTBotPreferences.SAROS_LONG_TIMEOUT); } /** * ******************************************** * * <p>Content of Saros View * * <p>******************************************** */ @Override public IContextMenusInContactListArea selectContacts() throws RemoteException { initContactsContextMenuWrapper(Pattern.quote((NODE_CONTACTS))); return ContextMenusInContactListArea.getInstance(); } @Override public IContextMenusInContactListArea selectContact(JID jid) throws RemoteException { if (getNickname(jid) == null) { throw new RuntimeException("no contact exists with the JID: " + jid.getBase()); } initContactsContextMenuWrapper( Pattern.quote(NODE_CONTACTS), Pattern.quote(getNickname(jid)) + ".*"); return ContextMenusInContactListArea.getInstance(); } @Override public IContextMenusInSessionArea selectSession() throws RemoteException { if (!isInSession()) throw new RuntimeException("you are not in a session"); initSessionContextMenuWrapper(Pattern.quote((NODE_SESSION))); return ContextMenusInSessionArea.getInstance(); } @Override public IContextMenusInSessionArea selectNoSessionRunning() throws RemoteException { if (isInSession()) throw new RuntimeException("you are in a session"); initSessionContextMenuWrapper(Pattern.quote((NODE_NO_SESSION_RUNNING))); return ContextMenusInSessionArea.getInstance(); } @Override public IContextMenusInSessionArea selectUser(final JID participantJID) throws RemoteException { if (!isInSession()) throw new IllegalStateException("you are not in a session"); String participantLabel = getLabelName(participantJID); initSessionContextMenuWrapper( Pattern.quote(NODE_SESSION), ".*" + Pattern.quote(participantLabel) + ".*"); ContextMenusInSessionArea.getInstance().setParticipantJID(participantJID); return ContextMenusInSessionArea.getInstance(); } /** * ******************************************** * * <p>state * * <p>******************************************** */ @Override public boolean isConnected() { return isToolbarButtonEnabled(TB_DISCONNECT); } public boolean isDisconnected() { return isToolbarButtonEnabled(TB_CONNECT); } @Override public String getNickname(JID jid) throws RemoteException { Roster roster = getConnectionService().getRoster(); if (roster == null) throw new IllegalStateException("not connected to a xmpp server"); if (roster.getEntry(jid.getBase()) == null) return null; if (roster.getEntry(jid.getBase()).getName() == null) return jid.getBase(); else return roster.getEntry(jid.getBase()).getName(); } @Override public boolean hasNickName(JID jid) throws RemoteException { try { if (getNickname(jid) == null) return false; if (!getNickname(jid).equals(jid.getBase())) return true; return false; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } @Override public List<String> getContacts() throws RemoteException { SWTBotTreeItem items = tree.getTreeItem(NODE_CONTACTS); List<String> contacts = new ArrayList<String>(); for (SWTBotTreeItem item : items.getItems()) contacts.add(item.getText()); return contacts; } @Override public boolean isInContactList(JID jid) throws RemoteException { try { String nickName = getNickname(jid); if (nickName == null) return false; nickName = Pattern.quote(nickName) + ".*"; for (String label : tree.getTreeItem(NODE_CONTACTS).getNodes()) if (label.matches(nickName)) return true; return false; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } @Override public boolean existsUser(JID jid) throws RemoteException { try { String participantLabel = getLabelName(jid); participantLabel = ".*" + Pattern.quote(participantLabel) + ".*"; for (String label : tree.getTreeItem(NODE_SESSION).getNodes()) if (label.matches(participantLabel)) return true; return false; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } @Override public boolean isInSession() { try { for (SWTBotToolbarButton button : view.getToolbarButtons()) { if ((button.getToolTipText().equals(TB_STOP_SESSION) || button.getToolTipText().equals(TB_LEAVE_SESSION)) && button.isEnabled()) return true; } return false; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } @Override public boolean isHost() throws RemoteException { try { if (!isInSession()) return false; List<String> participants = tree.getTreeItem(NODE_SESSION).getNodes(); if (participants.size() == 0) return false; for (String participant : participants) { if (participant.contains(HOST_INDICATION)) { if (participant.contains(getLabelName(SuperBot.getInstance().getJID()))) { return true; } } } return false; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } @Override public boolean isFollowing() throws RemoteException { try { JID followee = getFollowedUser(); if (followee == null) return false; return selectUser(followee).isFollowing(); } catch (Exception e) { log.error(e.getMessage(), e); return false; } } @Override public List<String> getUsers() throws RemoteException { return tree.getTreeItem(NODE_SESSION).getNodes(); } @Override public JID getFollowedUser() { FollowModeManager followModeManager = getFollowModeManager(); if (followModeManager == null) return null; User followedUser = followModeManager.getFollowedUser(); return (followedUser != null) ? followedUser.getJID() : null; } @Override public IChatroom selectChatroom(String name) throws RemoteException { return selectChatroomWithRegex(Pattern.quote(name)); } @SuppressWarnings("unchecked") @Override public IChatroom selectChatroomWithRegex(String regex) throws RemoteException { Chatroom.getInstance() .setChatTab( new SWTBotCTabItem( view.bot() .widget( allOf(widgetOfType(CTabItem.class), withRegex(regex)), view.getWidget()))); return Chatroom.getInstance(); } @Override public void closeChatroom(String name) throws RemoteException { closeChatroomWithRegex(Pattern.quote(name)); } @SuppressWarnings("unchecked") @Override public void closeChatroomWithRegex(String regex) throws RemoteException { List<? extends Widget> chatTabs = view.bot().widgets(allOf(widgetOfType(CTabItem.class), withRegex(regex)), view.getWidget()); for (Widget chatTab : chatTabs) new SWTBotCTabItem((CTabItem) chatTab).close(); } @SuppressWarnings("unchecked") @Override public boolean hasOpenChatrooms() throws RemoteException { return !view.bot() .getFinder() .findControls(view.getWidget(), allOf(widgetOfType(CTabItem.class), withRegex(".*")), true) .isEmpty(); } /* * waits until */ @Override public void waitUntilIsConnected() throws RemoteException { new SWTBot() .waitUntil( new DefaultCondition() { @Override public boolean test() throws Exception { return isConnected(); } @Override public String getFailureMessage() { return "unable to connect to server"; } }, SarosSWTBotPreferences.SAROS_DEFAULT_TIMEOUT); } @Override public void waitUntilIsDisconnected() throws RemoteException { new SWTBot() .waitUntil( new DefaultCondition() { @Override public boolean test() throws Exception { return isDisconnected(); } @Override public String getFailureMessage() { return "unable to disconnect from server"; } }, SarosSWTBotPreferences.SAROS_DEFAULT_TIMEOUT); } @Override public void waitUntilIsInSession() throws RemoteException { new SWTBot() .waitUntil( new DefaultCondition() { @Override public boolean test() throws Exception { return isInSession(); } @Override public String getFailureMessage() { return "joining the session failed"; } }); } @Override public void waitUntilIsInviteeInSession(ISuperBot sarosBot) throws RemoteException { sarosBot.views().sarosView().waitUntilIsInSession(); } @Override public void waitUntilIsNotInSession() throws RemoteException { new SWTBot() .waitUntil( new DefaultCondition() { @Override public boolean test() throws Exception { return !isInSession(); } @Override public String getFailureMessage() { return "leaving the session failed"; } }); } @Override public void waitUntilAllPeersLeaveSession(final List<JID> jidsOfAllParticipants) throws RemoteException { /* * see STFController which manipulates the behavior of how the * HostAloneInSession dialog is displayed */ // new SWTBot().waitUntil(new DefaultCondition() { // @Override // public boolean test() throws Exception { // for (JID jid : jidsOfAllParticipants) { // if (existsParticipant(jid)) // return false; // } // return true; // } // // @Override // public String getFailureMessage() { // return "there are still users in the session"; // } // }); waitUntilIsNotInSession(); } @Override public void waitUntilIsInconsistencyDetected() throws RemoteException { RemoteWorkbenchBot.getInstance() .waitUntil( new DefaultCondition() { @Override public boolean test() throws Exception { return WidgetUtil.getToolbarButtonWithRegex( view, Pattern.quote(TB_INCONSISTENCY_DETECTED) + ".*") .isEnabled(); } @Override public String getFailureMessage() { return "the toolbar button " + TB_INCONSISTENCY_DETECTED + " is not enabled"; } }); } /** * ************************************************************ * * <p>inner functions * * <p>************************************************************ */ private String getLabelName(JID jid) throws RemoteException { String contactLabel; if (SuperBot.getInstance().views().sarosView().hasNickName(jid)) { contactLabel = SuperBot.getInstance().views().sarosView().getNickname(jid); } else { contactLabel = jid.getName(); } return contactLabel; } private boolean isToolbarButtonEnabled(String tooltip) { try { for (SWTBotToolbarButton button : view.getToolbarButtons()) if (button.getToolTipText().equals(tooltip) && button.isEnabled()) return true; return false; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } private void clickToolbarButtonWithTooltip(String tooltipText) { WidgetUtil.getToolbarButtonWithRegex(view, Pattern.quote(tooltipText) + ".*").click(); } private void selectParticipant(JID jidOfSelectedUser, String message) throws RemoteException { if (SuperBot.getInstance().getJID().equals(jidOfSelectedUser)) { throw new RuntimeException(message); } selectUser(jidOfSelectedUser); } private void setViewWithTree(SWTBotView view) { this.view = view; this.tree = view.bot().tree(); } private void initSessionContextMenuWrapper(String... treeItemNodes) { ContextMenusInSessionArea.getInstance().setTree(tree); ContextMenusInSessionArea.getInstance().setTreeItemNodes(treeItemNodes); ContextMenusInSessionArea.getInstance().setSarosView(this); } private void initContactsContextMenuWrapper(String... treeItemNodes) { ContextMenusInContactListArea.getInstance().setTree(tree); ContextMenusInContactListArea.getInstance().setTreeItemNodes(treeItemNodes); ContextMenusInContactListArea.getInstance().setSarosView(this); } }