/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * IntellicutListModel.java * Creation date: Feb 21-2002 * By: David Mosimann, Frank Worsley */ package org.openquark.gems.client; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractListModel; import javax.swing.ComboBoxModel; import org.openquark.gems.client.IntellicutListModelAdapter.IntellicutListEntry; /** * Provides the list model and combobox model that are used by the intellicut list and combobox. * Not thread-safe. * @author Frank Worsley */ public class IntellicutListModel extends AbstractListModel implements ComboBoxModel { private static final long serialVersionUID = -1244914095475717587L; /** The adapter for this list model. */ private final IntellicutListModelAdapter adapter; /** The string of user input that has been typed in. */ private String userInput = null; /** The currently selected item in the list (needed for the ComboBoxModel interface). */ private Object selectedItem = null; /** the elements currently comprising the model */ private final List<IntellicutListEntry> elementsOfModel = new ArrayList<IntellicutListEntry>(); /** * Typesafe enum pattern representing the level of filtering to perform on Gems * in the Intellicut list panel. Values of FilterLevel can be compared using ==. * * Creation date: (Apr 27, 2005) * @author Jawright */ public static final class FilterLevel { /** Filter level to show all gems whose type fits */ public static final FilterLevel SHOW_ALL = new FilterLevel("ShowAll"); /** Filter level to show all gems whose typeCloseness is >0 or whose reference frequency is in the top 20% */ public static final FilterLevel SHOW_LIKELY = new FilterLevel("ShowLikely"); /** Filter level to show all gems whose typeCloseness is >0 and whose reference frequency is in the top 20% */ public static final FilterLevel SHOW_BEST = new FilterLevel("ShowBest"); /** @return The unique name of this FilterLevel */ @Override public String toString() { return uniqueID; } /** * Returns the FilterLevel that has the specified uniqueID * @param uniqueID String specifying the FilterLevel to return * @return FilterLevel with the given uniqueID */ public static FilterLevel fromString(String uniqueID) { if (uniqueID.equals("ShowAll")) { return SHOW_ALL; } else if (uniqueID.equals("ShowLikely")) { return SHOW_LIKELY; } else if (uniqueID.equals("ShowBest")) { return SHOW_BEST; } else { throw new IllegalArgumentException(uniqueID + " does not specify a valid FilterLevel"); } } /** The unique string name of this FilterLevel */ private final String uniqueID; /** * Constructor for FilterLevels. This constructor is private to prevent other * values of FilterLevel from being defined outside this class. * @param uniqueID ID that uniquely identifies this instance of FilterLevel */ private FilterLevel(String uniqueID) { this.uniqueID = uniqueID; } } /** Level of filtering to apply to what is shown in the list */ private FilterLevel filterLevel = FilterLevel.SHOW_ALL; /** * Constructor for a new IntellicutListModel. * @param adapter the adapter for the list model */ public IntellicutListModel(IntellicutListModelAdapter adapter) { if (adapter == null) { throw new NullPointerException(); } this.adapter = adapter; } /** * @return the adapter used by this model */ public IntellicutListModelAdapter getAdapter() { return adapter; } /** * Initially loads the list of available list entries. * Does nothing if the list is already loaded. */ public void load() { if (adapter.getListEntrySet().isEmpty()) { adapter.load(); } else { refreshList(userInput); } } /** * Refreshes the displayed list of items so that they match the specified user input. * @param userInput the prefix they have to match */ public void refreshList(String userInput) { refreshList(userInput, filterLevel); } /** * Refreshes the displayed list of items so that they match the specified input * and whether or not all items should be shown. * @param userInput the prefix they have to match * @param filterLevel whether to show all items or only items with type closeness > 0 */ private void refreshList (String userInput, FilterLevel filterLevel) { this.userInput = userInput; this.filterLevel = filterLevel; // Load the list of all gems if it has not been loaded yet. if (adapter.getListEntrySet().isEmpty()) { load(); } // Clear our current subset of gems. elementsOfModel.clear(); // Add back all gems that match the new criteria. for (final IntellicutListEntry listEntry : adapter.getListEntrySet()) { String sortString = listEntry.getSortString(); String namePrefix = null; if (userInput == null) { userInput = ""; } if (userInput.length() > 0 && userInput.length() <= sortString.length()) { namePrefix = sortString.substring(0, userInput.length()); } // Only include the entry in the current model subset if the input length is zero and // it is a best entry or if the prefix matches the user input. if ((adapter.passesFilter(listEntry, filterLevel) && userInput.length() == 0) || userInput.equalsIgnoreCase(namePrefix)) { elementsOfModel.add(listEntry); } } fireContentsChanged(this, 0, elementsOfModel.size()); } /** * Returns the user input with which the model was last refreshed. * @return the user input string */ public String getUserInput() { return userInput; } /** * Returns the current filtering level * @return FilterLevel Current level of filtering */ public FilterLevel getFilterLevel() { return filterLevel; } /** * Sets the level at which the list of gems should be filtered * @param newLevel int level to filter at (SHOW_ALL, SHOW_LIKELY, or SHOW_BEST) */ public void setFilterLevel(FilterLevel newLevel) { if (filterLevel == null) { throw new NullPointerException("filterLevel must not be null in IntellicutListModel.setFilterLevel"); } if (filterLevel != newLevel) { filterLevel = newLevel; refreshList(userInput, filterLevel); } } /** * Finds the first list entry in the model that matches the given prefix. * This searches the currently visible entries, not the entire set of entries. * @param prefix the prefix to check for * @return the first list entry that matches, null if none matches */ IntellicutListEntry getFirstMatchFor(String prefix) { if (getSize() == 0) { return null; } if (prefix.length() == 0) { return (IntellicutListEntry) getElementAt(0); } for (final IntellicutListEntry listEntry : elementsOfModel) { // get the name of the gem we are currently traversing. String entryName = listEntry.getSortString(); // Make sure the input length is shorter than the name. if (prefix.length() <= entryName.length()) { // Return the first match. if (entryName.substring(0, prefix.length()).equalsIgnoreCase(prefix)) { return listEntry; } } } return null; } /** * Returns whether or not the there exists a list entry that has the given name prefix. * This searches the entire list of list entries, not just the currently visible subset. * @param prefix the required name prefix (case-insensitive) * @param filterLevel The level of filter that the gems must pass to count * @return true if a match is found, false otherwise */ private boolean hasMatchesFor(String prefix, FilterLevel filterLevel) { if (adapter.getListEntrySet().isEmpty()) { return false; } if (prefix.length() == 0 && filterLevel == FilterLevel.SHOW_ALL) { return true; } for (final IntellicutListEntry listEntry : adapter.getListEntrySet()) { String entryName = listEntry.getSortString(); // Make sure the input length is shorter than the name. if (prefix.length() <= entryName.length()) { // Even if there is a single match, we return true. if (adapter.passesFilter(listEntry, filterLevel) && entryName.substring(0, prefix.length()).equalsIgnoreCase(prefix)) { return true; } } } return false; } /** * Returns whether or not there is any list entry that has the given prefix. * @param prefix the required name prefix (case-insensitive) * @return true if a match is found, false otherwise */ boolean hasAnyGemsFor(String prefix) { return hasMatchesFor(prefix, FilterLevel.SHOW_ALL); } /** * Returns whether or not there exists a list entry that has the given name prefix and * a type closeness of at least 1. * @param prefix the desired name prefix (case-insensitive) * @return true if a match is found, false otherwise */ boolean hasFilteredGemsFor(String prefix, FilterLevel filterLevel) { return hasMatchesFor(prefix, filterLevel); } /** * Return the number list entries that pass the specified filter level * @param filterLevel filter level to check for (SHOW_ALL, SHOW_LIKELY, or SHOW_BEST) * @return number of list entries that pass the specified filter */ int numFilteredGems(FilterLevel filterLevel) { // Scan through the list of entries counting up the number of passes int passCount = 0; for (final IntellicutListEntry listEntry : adapter.getListEntrySet()) { if (adapter.passesFilter(listEntry, filterLevel)) { passCount++; } } return passCount; } /** * Implemented for the ComboBoxModel interface. * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object) */ public void setSelectedItem(Object selectedItem) { this.selectedItem = selectedItem; } /** * Implemented for the ComboBoxModel interface. * @see javax.swing.ComboBoxModel#getSelectedItem() */ public Object getSelectedItem() { return selectedItem; } /** * {@inheritDoc} */ public Object getElementAt(int index) { return elementsOfModel.get(index); } /** * @param index the index of the entry to get. * @return the entry at the given index. */ public IntellicutListEntry get(int index) { return (IntellicutListEntry)getElementAt(index); } /** * {@inheritDoc} */ public int getSize() { return elementsOfModel.size(); } /** * @return whether the list model is empty. */ public boolean isEmpty() { return elementsOfModel.isEmpty(); } /** * @param entry the entry for which the index should be found. * @return the index of the entry in the current list model, or -1 if the entry is not a member of the model. */ public int indexOf(IntellicutListEntry entry) { return elementsOfModel.indexOf(entry); } }