package org.chartsy.chatsy.chat.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.chartsy.chatsy.MainWindow;
import org.chartsy.chatsy.Chatsy;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromContainsFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;
import org.chartsy.chatsy.chat.ChatsyManager;
import org.chartsy.chatsy.chat.component.tabbedPane.ChatsyTab;
import org.chartsy.chatsy.chat.component.tabbedPane.ChatsyTabbedPane;
import org.chartsy.chatsy.chat.component.tabbedPane.ChatsyTabbedPaneListener;
import org.chartsy.chatsy.chat.ui.rooms.ChatRoomImpl;
import org.chartsy.chatsy.chat.ui.rooms.GroupChatRoom;
import org.chartsy.chatsy.chat.util.SwingTimerTask;
import org.chartsy.chatsy.chat.util.TaskEngine;
public class ChatContainer extends ChatsyTabbedPane
implements MessageListener, ChangeListener
{
private static final Logger LOG = Logger.getLogger(ChatContainer.class.getName());
private final List<ChatRoomListener> chatRoomListeners = new ArrayList<ChatRoomListener>();
private final List<ChatRoom> chatRoomList = new ArrayList<ChatRoom>();
private final Map<String, PacketListener> presenceMap = new HashMap<String, PacketListener>();
private static final String WELCOME_TITLE = "welcome";
private ChatFrame chatFrame;
private final TimerTask focusTask;
public ChatContainer()
{
super(JTabbedPane.TOP);
addChatsyTabbedPaneListener(new ChatsyTabbedPaneListener()
{
@Override public void tabRemoved(ChatsyTab tab, Component component, int index)
{
stateChanged(null);
if (component instanceof ChatRoom)
cleanupChatRoom((ChatRoom)component);
else if (component instanceof ContainerComponent)
((ContainerComponent)component).closing();
}
@Override public void tabAdded(ChatsyTab tab, Component component, int index)
{
stateChanged(null);
}
@Override public void tabSelected(ChatsyTab tab, Component component, int index)
{
stateChanged(null);
if (component instanceof ChatRoom)
fireChatRoomActivated((ChatRoom)component);
}
@Override public void allTabsRemoved()
{
chatFrame.setTitle("");
chatFrame.setVisible(false);
}
@Override public boolean canTabClose(ChatsyTab tab, Component component)
{
return true;
}
});
setCloseButtonEnabled(true);
setFocusable(false);
setOpaque(true);
setBackground(Color.white);
focusTask = new SwingTimerTask()
{
@Override public void doRun()
{
try
{
ChatRoom chatRoom = getActiveChatRoom();
chatRoom.requestFocusInWindow();
chatRoom.getChatInputEditor().requestFocusInWindow();
}
catch (ChatRoomNotFoundException e)
{
}
}
};
}
public synchronized void addChatRoom(final ChatRoom room)
{
createFrameIfNeeded();
AndFilter presenceFilter = new AndFilter(new PacketTypeFilter(Presence.class), new FromContainsFilter(room.getRoomname()));
PacketListener myListener = new PacketListener()
{
@Override public void processPacket(final Packet packet)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override public void run()
{
handleRoomPresence((Presence)packet);
}
});
}
};
ChatsyManager.getConnection().addPacketListener(myListener, presenceFilter);
presenceMap.put(room.getRoomname(), myListener);
String tooltip;
if (room instanceof ChatRoomImpl)
{
tooltip = ((ChatRoomImpl)room).getParticipantJID();
String nickname = ChatsyManager.getUserManager().getUserNicknameFromJID(((ChatRoomImpl)room).getParticipantJID());
tooltip = "<html><body><b>Contact: </b>" + nickname + "<br><b>JID: </b>" + tooltip;
}
else
{
tooltip = room.getRoomname();
}
ChatsyTab tab = addTab(room.getTabTitle(), room.getTabIcon(), room, tooltip);
tab.addMouseListener(new MouseAdapter()
{
@Override public void mouseReleased(MouseEvent e)
{
checkTabPopup(e);
}
@Override public void mousePressed(MouseEvent e)
{
checkTabPopup(e);
}
});
room.addMessageListener(this);
final String title = getTabAt(0).getActualText();
if (title.equals(WELCOME_TITLE))
chatFrame.setTitle(room.getRoomTitle());
final TimerTask visibleTask = new SwingTimerTask()
{
@Override public void doRun()
{
checkVisibility(room);
}
};
TaskEngine.getInstance().schedule(visibleTask, 100);
chatRoomList.add(room);
fireChatRoomOpened(room);
focusChat();
}
public void addContainerComponent(ContainerComponent comp)
{
createFrameIfNeeded();
addTab(comp.getTabTitle(), comp.getTabIcon(), comp.getGUI(), comp.getToolTipDescription());
chatFrame.setTitle(comp.getFrameTitle());
checkVisibility(comp.getGUI());
if (getSelectedComponent() != comp)
ChatsyManager.getChatManager().notifyChatsyTabHandlers(comp.getGUI());
}
private void handleRoomPresence(final Presence p)
{
final String roomname = StringUtils.parseBareAddress(p.getFrom());
ChatRoom chatRoom = getChatRoom(roomname);
if (chatRoom == null)
return;
final String userid = StringUtils.parseResource(p.getFrom());
if (p.getType() == Presence.Type.unavailable)
fireUserHasLeft(chatRoom, userid);
else if (p.getType() == Presence.Type.available)
fireUserHasJoined(chatRoom, userid);
if (chatRoom instanceof ChatRoomImpl)
{
int tabLoc = indexOfComponent(chatRoom);
if (tabLoc != -1)
ChatsyManager.getChatManager().notifyChatsyTabHandlers(chatRoom);
}
}
private void checkVisibility(Component component)
{
if (!chatFrame.isVisible() && ChatsyManager.getMainWindow().isFocusOwner())
{
chatFrame.setState(Frame.NORMAL);
chatFrame.setVisible(true);
}
else if (chatFrame.isVisible() && !chatFrame.isInFocus())
{
startFlashing(component, false, null, null);
}
else if (chatFrame.isVisible() && chatFrame.getState() == Frame.ICONIFIED)
{
int tabLocation = indexOfComponent(component);
setSelectedIndex(tabLocation);
startFlashing(component, false, null, null);
}
else if (chatFrame.isVisible() && !ChatsyManager.getMainWindow().isVisible())
{
startFlashing(component, false, null, null);
}
else if (!chatFrame.isVisible())
{
int tabLocation = indexOfComponent(component);
setSelectedIndex(tabLocation);
if (Chatsy.isWindows())
{
chatFrame.setFocusableWindowState(false);
chatFrame.setState(Frame.ICONIFIED);
}
chatFrame.setVisible(true);
chatFrame.setFocusableWindowState(true);
if (!ChatsyManager.getMainWindow().isVisible())
startFlashing(component, false, null, null);
else if (chatFrame.getState() == Frame.ICONIFIED)
startFlashing(component, false, null, null);
if (component instanceof ChatRoom)
chatFrame.setTitle(((ChatRoom)component).getRoomTitle());
}
}
private void handleMessageNotification(final ChatRoom chatRoom, boolean customMsg, String customMsgText, String customMsgTitle)
{
ChatRoom activeChatRoom = null;
try
{
activeChatRoom = getActiveChatRoom();
}
catch (ChatRoomNotFoundException e)
{
LOG.log(Level.SEVERE, "", e);
}
if (chatFrame.isVisible()
&& (chatFrame.getState() == Frame.ICONIFIED
|| chatFrame.getInactiveTime() > 20000))
{
int tabLocation = indexOfComponent(chatRoom);
setSelectedIndex(tabLocation);
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
return;
}
if (!chatFrame.isVisible()
&& ChatsyManager.getMainWindow().isFocusOwner())
{
chatFrame.setState(Frame.NORMAL);
chatFrame.setVisible(true);
}
else if (chatFrame.isVisible()
&& !chatFrame.isInFocus())
{
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
}
else if (chatFrame.isVisible()
&& chatFrame.getState() == Frame.ICONIFIED)
{
int tabLocation = indexOfComponent(chatRoom);
setSelectedIndex(tabLocation);
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
}
else if (chatFrame.isVisible()
&& !ChatsyManager.getMainWindow().isVisible()
&& !chatFrame.isInFocus())
{
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
}
else if (!chatFrame.isVisible())
{
int tabLocation = indexOfComponent(chatRoom);
setSelectedIndex(tabLocation);
if (Chatsy.isWindows())
chatFrame.setExtendedState(Frame.ICONIFIED);
chatFrame.setVisible(true);
if (!ChatsyManager.getMainWindow().isVisible())
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
else if (chatFrame.getState() == Frame.ICONIFIED)
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
chatFrame.setTitle(chatRoom.getRoomTitle());
}
else if (chatRoom != activeChatRoom)
{
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
}
}
private void cleanupChatRoom(ChatRoom room)
{
if (room.isActive())
{
room.leaveChatRoom();
room.closeChatRoom();
}
final PacketListener listener = presenceMap.get(room.getRoomname());
if (listener != null)
ChatsyManager.getConnection().removePacketListener(listener);
fireChatRoomClosed(room);
room.removeMessageListener(this);
presenceMap.remove(room.getRoomname());
chatRoomList.remove(room);
room.getTranscriptWindow().cleanup();
}
public void closeAllChatRooms()
{
for (ChatRoom chatRoom : new ArrayList<ChatRoom>(chatRoomList))
{
closeTab(chatRoom);
chatRoom.closeChatRoom();
}
for (int i = 0; i < getTabCount(); i++)
{
Component comp = getComponentAt(i);
if (comp instanceof ContainerComponent)
((ContainerComponent)comp).closing();
closeTab(comp);
}
}
public void leaveChatRoom(ChatRoom room)
{
fireChatRoomLeft(room);
room.leaveChatRoom();
final PacketListener listener = presenceMap.get(room.getRoomname());
if (listener != null
&& ChatsyManager.getConnection().isConnected())
ChatsyManager.getConnection().removePacketListener(listener);
}
public ChatRoom getChatRoom(String roomName)
{
for (int i = 0; i < getTabCount(); i++)
{
ChatRoom room = getChatRoom(i);
if (room != null
&& room.getRoomname().equalsIgnoreCase(roomName)
&& room.isActive())
return room;
}
return null;
}
public ChatRoom getChatRoom(int location)
{
if (getTabCount() < location)
return null;
try
{
Component comp = getComponentAt(location);
if (comp instanceof ChatRoom)
return (ChatRoom)comp;
}
catch (ArrayIndexOutOfBoundsException outOfBoundsEx)
{
LOG.log(Level.SEVERE, "", outOfBoundsEx);
}
return null;
}
public ChatRoom getActiveChatRoom() throws ChatRoomNotFoundException
{
int location = getSelectedIndex();
if (location != -1)
return getChatRoom(location);
throw new ChatRoomNotFoundException();
}
public Component getActiveRoom()
{
int location = getSelectedIndex();
if (location != -1)
return getComponentAt(location);
return null;
}
public void activateChatRoom(ChatRoom room)
{
int tabLocation = indexOfComponent(room);
setSelectedIndex(tabLocation);
chatFrame.bringFrameIntoFocus();
focusChat();
}
public void activateComponent(Component component)
{
int tabLocation = indexOfComponent(component);
if (tabLocation != -1)
setSelectedIndex(tabLocation);
chatFrame.bringFrameIntoFocus();
focusChat();
}
public void messageReceived(ChatRoom room, Message message)
{
room.increaseUnreadMessageCount();
fireNotifyOnMessage(room, false, null, null);
}
public void fireNotifyOnMessage(final ChatRoom chatRoom, final boolean customMsg, final String customMsgText, final String customMsgTitle)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override public void run()
{
handleMessageNotification(chatRoom, customMsg, customMsgText, customMsgTitle);
}
});
}
public void messageSent(ChatRoom room, Message message)
{
fireChatRoomStateUpdated(room);
}
public void stateChanged(ChangeEvent e)
{
if (chatFrame.isInFocus())
stopFlashing();
final Object o = getSelectedComponent();
if (o instanceof ChatRoom)
{
final ChatRoom room = (ChatRoom)o;
focusChat();
chatFrame.setTitle(room.getRoomTitle());
}
else if (o instanceof ContainerComponent)
{
final ContainerComponent comp = (ContainerComponent)o;
chatFrame.setTitle(comp.getFrameTitle());
chatFrame.setIconImage(comp.getTabIcon().getImage());
ChatsyManager.getChatManager().notifyChatsyTabHandlers(comp.getGUI());
}
}
private void stopFlashing()
{
try
{
int selectedIndex = getSelectedIndex();
if (selectedIndex != -1)
{
Component comp = getComponentAt(selectedIndex);
if (comp != null)
stopFlashing(comp);
}
}
catch (Exception e)
{
LOG.log(Level.SEVERE, "", e);
}
}
public void closeTab(Component component)
{
int location = indexOfComponent(component);
if (location == -1)
return;
if (getTabCount() == 0)
chatFrame.setTitle("");
this.removeTabAt(location);
}
public void closeActiveRoom()
{
ChatRoom room;
try
{
room = getActiveChatRoom();
}
catch (ChatRoomNotFoundException e)
{
Component comp = getActiveRoom();
if (comp != null)
{
boolean canClose = ((ContainerComponent)comp).closing();
if (canClose)
closeTab(comp);
}
return;
}
boolean isGroupChat = room.getChatType() == Message.Type.groupchat;
if (isGroupChat)
{
final int ok = JOptionPane.showConfirmDialog(
chatFrame,
"Would you like to end this session?",
"Confirmation",
JOptionPane.YES_NO_OPTION);
if (ok == JOptionPane.OK_OPTION)
room.closeChatRoom();
}
else
{
room.closeChatRoom();
}
}
@Override public String toString()
{
StringBuilder buf = new StringBuilder();
for (ChatRoom room : chatRoomList)
buf.append("Roomname=").append(room.getRoomname()).append("\n");
return buf.toString();
}
public boolean hasRooms()
{
int count = getSelectedIndex();
return count != -1;
}
public void addChatRoomListener(ChatRoomListener listener)
{
if (!chatRoomListeners.contains(listener))
chatRoomListeners.add(listener);
}
public void removeChatRoomListener(ChatRoomListener listener)
{
chatRoomListeners.remove(listener);
}
protected void fireChatRoomOpened(ChatRoom room)
{
for (ChatRoomListener chatRoomListener : new ArrayList<ChatRoomListener>(chatRoomListeners))
chatRoomListener.chatRoomOpened(room);
}
protected void fireChatRoomLeft(ChatRoom room)
{
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners))
chatRoomListener.chatRoomLeft(room);
}
protected void fireChatRoomClosed(ChatRoom room)
{
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners))
chatRoomListener.chatRoomClosed(room);
}
protected void fireChatRoomActivated(ChatRoom room)
{
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners))
chatRoomListener.chatRoomActivated(room);
}
protected void fireUserHasJoined(final ChatRoom room, final String userid)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override public void run()
{
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners))
chatRoomListener.userHasJoined(room, userid);
}
});
}
protected void fireUserHasLeft(final ChatRoom room, final String userid)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override public void run()
{
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners))
chatRoomListener.userHasLeft(room, userid);
}
});
}
public void startFlashing(final Component comp, final boolean customMsg, final String customMsgText, final String customMsgTitle)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override public void run()
{
try
{
final int index = indexOfComponent(comp);
if (index != -1)
{
if (!(comp instanceof GroupChatRoom))
{
if (comp instanceof ChatRoom)
{
if(comp instanceof GroupChatRoom)
{
if(!((GroupChatRoom) comp).isBlocked(((GroupChatRoom) comp).getLastMessage().getFrom()))
checkNotificationPreferences((ChatRoom)comp, customMsg, customMsgText, customMsgTitle);
}
else
{
checkNotificationPreferences((ChatRoom)comp, customMsg, customMsgText, customMsgTitle);
}
}
}
ChatsyManager.getChatManager().notifyChatsyTabHandlers(comp);
}
if (!chatFrame.isInFocus())
ChatsyManager.getNativeManager().flashWindow(chatFrame);
}
catch (Exception ex)
{
LOG.log(Level.SEVERE, "", ex);
}
}
});
}
public void fireChatRoomStateUpdated(final ChatRoom room)
{
final int index = indexOfComponent(room);
if (index != -1)
ChatsyManager.getChatManager().notifyChatsyTabHandlers(room);
}
public void stopFlashing(final Component component)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override public void run()
{
try
{
ChatsyManager.getNativeManager().stopFlashing(chatFrame);
ChatsyManager.getChatManager().notifyChatsyTabHandlers(component);
}
catch (Exception ex)
{
LOG.log(Level.SEVERE, "", ex);
}
}
});
}
private void checkNotificationPreferences(final ChatRoom room, boolean customMsg, String customMsgText, String customMsgTitle)
{
chatFrame.setState(Frame.NORMAL);
chatFrame.setVisible(true);
}
private void groupChatMessageCheck(ChatRoom chatRoom, boolean customMsg, String customMsgText, String customMsgTitle)
{
boolean isGroupChat = chatRoom.getChatType() == Message.Type.groupchat;
int size = chatRoom.getTranscripts().size();
if (isGroupChat)
{
Message lastChatMessage = chatRoom.getTranscripts().get(size - 1);
String mucNickNameT = lastChatMessage.getFrom();
String[] mucNickName = mucNickNameT.split("/");
String finalRoomName = chatRoom.getRoomTitle();
String myNickName = chatRoom.getNickname();
String myUserName = ChatsyManager.getSessionManager().getUsername();
Pattern usernameMatch = Pattern.compile(myUserName, Pattern.CASE_INSENSITIVE);
Pattern nicknameMatch = Pattern.compile(myNickName, Pattern.CASE_INSENSITIVE);
if (usernameMatch.matcher(lastChatMessage.getBody()).find()
|| nicknameMatch.matcher(lastChatMessage.getBody()).find())
{
boolean customMsgS = true;
String customMsgTextS = "Your name has been said in group chat: "
+ finalRoomName + " by " + mucNickName[1] + " (" + lastChatMessage + ")";
String customMsgTitleS = "Seen your name...";
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
}
else
{
boolean customMsgS = true;
String customMsgTextS = mucNickName[1] + " says: " + lastChatMessage.getBody();
String customMsgTitleS = finalRoomName;
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
}
}
else if (customMsg)
{
boolean customMsgS = customMsg;
String customMsgTextS = customMsgText;
String customMsgTitleS = customMsgTitle;
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
}
else
{
Message lastChatMessage = chatRoom.getTranscripts().get(size - 1);
String finalRoomName = chatRoom.getRoomTitle();
boolean customMsgS = true;
String customMsgTextS = lastChatMessage.getBody();
String customMsgTitleS = finalRoomName;
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
}
}
public void setChatRoomTitle(ChatRoom room, String title)
{
int index = indexOfComponent(room);
if (index != -1)
{
ChatsyTab tab = getTabAt(index);
fireChatRoomStateUpdated(room);
tab.setTabTitle(room.getTabTitle());
}
}
private void createFrameIfNeeded()
{
if (chatFrame != null)
return;
chatFrame = new ChatFrame();
chatFrame.addWindowListener(new WindowAdapter()
{
@Override public void windowActivated(WindowEvent windowEvent)
{
stopFlashing();
int sel = getSelectedIndex();
if (sel == -1)
return;
final ChatRoom room = getChatRoom(sel);
if (room != null)
{
focusChat();
chatFrame.setTitle(room.getRoomTitle());
}
}
@Override public void windowDeactivated(WindowEvent windowEvent)
{
}
@Override public void windowClosing(WindowEvent windowEvent)
{
ChatsyManager.getChatManager().getChatContainer().closeAllChatRooms();
chatFrame.setVisible(false);
}
});
}
public void focusChat()
{
TaskEngine.getInstance().schedule(focusTask, 50);
}
public Collection<ChatRoom> getChatRooms()
{
return new ArrayList<ChatRoom>(chatRoomList);
}
public ChatFrame getChatFrame()
{
return chatFrame;
}
public void blinkFrameIfNecessary(final JFrame frame)
{
final MainWindow mainWindow = ChatsyManager.getMainWindow();
if (mainWindow.isFocusOwner())
{
frame.setVisible(true);
}
else
{
if (Chatsy.isWindows())
{
frame.setState(Frame.ICONIFIED);
ChatsyManager.getNativeManager().flashWindow(frame);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter()
{
@Override public void windowActivated(WindowEvent e)
{
ChatsyManager.getNativeManager().stopFlashing(frame);
}
});
}
}
}
private void checkTabPopup(MouseEvent e)
{
final ChatsyTab tab = (ChatsyTab)e.getSource();
if (!e.isPopupTrigger())
return;
final JPopupMenu popup = new JPopupMenu();
Action closeThisAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
ChatRoom chatRoom = (ChatRoom)getComponentInTab(tab);
if (chatRoom != null)
closeTab(chatRoom);
}
};
closeThisAction.putValue(Action.NAME, "Close this chat");
popup.add(closeThisAction);
if (getChatRooms().size() > 1)
{
Action closeOthersAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
ChatRoom chatRoom = (ChatRoom)getComponentInTab(tab);
if (chatRoom != null)
for (ChatRoom cRoom : getChatRooms())
if (chatRoom != cRoom)
closeTab(cRoom);
}
};
closeOthersAction.putValue(Action.NAME, "Close other chats");
popup.add(closeOthersAction);
}
popup.show(tab, e.getX(), e.getY());
}
public int getTotalNumberOfUnreadMessages()
{
int messageCount = 0;
for (ChatRoom chatRoom : chatRoomList)
messageCount += chatRoom.getUnreadMessageCount();
return messageCount;
}
}