Java tutorial
/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.propertieseditor; import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.event.*; import javax.swing.plaf.*; import javax.swing.table.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.plugin.desktoputil.plaf.*; import net.java.sip.communicator.util.skin.*; import org.apache.commons.lang3.*; import org.jitsi.service.configuration.*; /** * The field used for searching in the properties table. * * @author Marin Dzhigarov */ public class SearchField extends SIPCommTextField implements Skinnable { /** * Serial Version UID. */ private static final long serialVersionUID = 1L; /** * The number of milliseconds that {@link #filterThread} is allowed to stay * alive idle i.e. without performing any filtering. After the timeout * elapses, <tt>filterThread</tt> will commit suicide. */ private static final long FILTER_THREAD_TIMEOUT = 5 * 60 * 1000; /** * Class id key used in UIDefaults. */ private static final String uiClassID = SearchField.class.getName() + "FieldUI"; /** * Adds the ui class to UIDefaults. */ static { UIManager.getDefaults().put(uiClassID, SearchFieldUI.class.getName()); } private final ConfigurationService confService = PropertiesEditorActivator.getConfigurationService(); /** * The <tt>String</tt> that filters the <tt>ConfigurationService</tt> * properties to be displayed. */ private String filter; /** * The <tt>Object</tt> which is to be used for synchronization purposes * related to <tt>ConfigurationService</tt> property filtering (e.g. * {@link #filter}, {@link #filterThread}). */ private final Object filterSyncRoot = new Object(); private Thread filterThread; /** * The table on which the search will be performed. */ private final JTable table; /** * Creates an instance <tt>SearchField</tt>. * * @param text the text we would like to enter by default * @param sorter the sorter which will be used for filtering. */ public SearchField(String text, JTable table) { super(text); this.table = table; ComponentUI ui = getUI(); if (ui instanceof SearchFieldUI) { SearchFieldUI searchFieldUI = (SearchFieldUI) ui; searchFieldUI.setCallButtonEnabled(false); searchFieldUI.setDeleteButtonEnabled(true); } setBorder(null); setDragEnabled(true); setOpaque(false); setPreferredSize(new Dimension(100, 25)); InputMap imap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "escape"); ActionMap amap = getActionMap(); @SuppressWarnings("serial") AbstractAction escapeAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { setText(""); } }; amap.put("escape", escapeAction); getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { } public void insertUpdate(DocumentEvent e) { filter(getText()); } public void removeUpdate(DocumentEvent e) { filter(getText()); } }); loadSkin(); } /** * Schedules a filter to be applied to <tt>ConfigurationService</tt> * properties so that only the ones matching the specified <tt>filter</tt> * will be displayed. * * @param filter the <tt>String</tt> to filter the * <tt>ConfigurationService</tt> properties to be displayed */ private void filter(String filter) { synchronized (filterSyncRoot) { this.filter = filter; if (filterThread == null) { filterThread = new Thread() { @Override public void run() { try { runInFilterThread(); } finally { /* * XXX Making sure here that filterThread is * aware of its death is just a precaution for * the cases of abnormal execution of the method * runInFilterThread(); otherwise, it's * insufficient in terms of synchronization * because the lock on filterSyncRoot has been * relinquished and the method * runInFilterThread() should take care of * making sure that filterThread is aware of its * death while it holds filterSyncRoot and has * determined that filterThread has idled too * long. */ synchronized (filterSyncRoot) { if (Thread.currentThread().equals(filterThread)) { filterThread = null; } } } } }; filterThread.setDaemon(true); filterThread.setName(SearchField.class.getName() + ".filterThread"); filterThread.start(); } else { filterSyncRoot.notify(); } } } /** * Returns the name of the L&F class that renders this component. * * @return the string "TreeUI" * @see JComponent#getUIClassID * @see UIDefaults#getUI */ public String getUIClassID() { return uiClassID; } /** * Reloads text field UI defs. */ public void loadSkin() { ComponentUI ui = getUI(); if (ui instanceof SearchFieldUI) ((SearchFieldUI) ui).loadSkin(); } /** * Runs in {@link #filterThread} to apply {@link #filter} to the displayed * <tt>ConfigurationService</tt> properties. */ private void runInFilterThread() { String prevFilter = null; long prevFilterTime = 0; do { final String filter; synchronized (filterSyncRoot) { filter = this.filter; /* * If the currentThread is idle for too long (which also means * that the filter has not been changed), kill it because we do * not want to keep it alive forever. */ if ((prevFilterTime != 0) && StringUtils.equalsIgnoreCase(filter, prevFilter)) { long timeout = FILTER_THREAD_TIMEOUT - (System.currentTimeMillis() - prevFilterTime); if (timeout > 0) { // The currentThread has been idle but not long enough. try { filterSyncRoot.wait(timeout); continue; } catch (InterruptedException ie) { // The currentThread will die bellow at the break. } } // Commit suicide. if (Thread.currentThread().equals(filterThread)) filterThread = null; break; } } List<String> properties = confService.getAllPropertyNames(); final List<Object[]> rows = new ArrayList<Object[]>(properties.size()); for (String property : properties) { String value = (String) confService.getProperty(property); if ((filter == null) || StringUtils.containsIgnoreCase(property, filter) || StringUtils.containsIgnoreCase(value, filter)) { rows.add(new Object[] { property, confService.getProperty(property) }); } } // If in the meantime someone has changed the filter, we don't want // to update the GUI but filter the results again. if (StringUtils.equalsIgnoreCase(filter, this.filter)) { LowPriorityEventQueue.invokeLater(new Runnable() { public void run() { DefaultTableModel model = (DefaultTableModel) table.getModel(); model.setRowCount(0); for (Object[] row : rows) { model.addRow(row); if (filter != SearchField.this.filter) return; } } }); } prevFilter = filter; prevFilterTime = System.currentTimeMillis(); } while (true); } }