package org.limewire.ui.swing.search.resultpanel.list; import static org.limewire.ui.swing.search.resultpanel.list.ListViewRowHeightRule.RowDisplayConfig.HeadingAndMetadata; import static org.limewire.ui.swing.search.resultpanel.list.ListViewRowHeightRule.RowDisplayConfig.HeadingAndSubheading; import static org.limewire.ui.swing.search.resultpanel.list.ListViewRowHeightRule.RowDisplayConfig.HeadingOnly; import static org.limewire.ui.swing.search.resultpanel.list.ListViewRowHeightRule.RowDisplayConfig.HeadingSubHeadingAndMetadata; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.limewire.core.api.FilePropertyKey; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; import org.limewire.ui.swing.search.model.BasicDownloadState; import org.limewire.ui.swing.search.model.VisualSearchResult; public class ListViewRowHeightRuleImpl implements ListViewRowHeightRule { private static final Log LOG = LogFactory.getLog(ListViewRowHeightRuleImpl.class); private static final String OPEN_HTML = "<html>"; private static final String CLOSE_HTML = "</html>"; private static final String EMPTY_STRING = ""; private final List<FilePropertyKey> audioKeyOrder; private final List<FilePropertyKey> videoKeyOrder; private final List<FilePropertyKey> documentKeyOrder; private final List<FilePropertyKey> programKeyOrder; public ListViewRowHeightRuleImpl() { List<FilePropertyKey> keys = new ArrayList<FilePropertyKey>(Arrays.asList(FilePropertyKey.values())); Collections.sort(keys, new PropertyKeyComparator(FilePropertyKey.GENRE, FilePropertyKey.BITRATE, FilePropertyKey.TRACK_NUMBER)); audioKeyOrder = new ArrayList<FilePropertyKey>(keys); Collections.sort(keys, new PropertyKeyComparator(FilePropertyKey.YEAR, FilePropertyKey.RATING, FilePropertyKey.DESCRIPTION, FilePropertyKey.HEIGHT, FilePropertyKey.WIDTH, FilePropertyKey.BITRATE)); videoKeyOrder = new ArrayList<FilePropertyKey>(keys); Collections.sort(keys, new PropertyKeyComparator(FilePropertyKey.DATE_CREATED, FilePropertyKey.AUTHOR)); documentKeyOrder = new ArrayList<FilePropertyKey>(keys); Collections.sort(keys, new PropertyKeyComparator(FilePropertyKey.PLATFORM, FilePropertyKey.COMPANY)); programKeyOrder = new ArrayList<FilePropertyKey>(keys); } private SearchHighlightUtil searchHighlightUtil; private String searchText; @Override public void initializeWithSearch(String search) { searchText = search; if(searchText != null) { searchHighlightUtil = new SearchHighlightUtil(search); } } @Override public RowDisplayResult getDisplayResult(VisualSearchResult vsr) { if (vsr.isSpam()) { return new RowDisplayResultImpl(HeadingOnly, vsr.getHeading(), null, null, vsr.isSpam(), vsr.getDownloadState()); } switch(vsr.getDownloadState()) { case DOWNLOADING: case DOWNLOADED: case LIBRARY: return new RowDisplayResultImpl(HeadingOnly, vsr.getHeading(), null, null, vsr.isSpam(), vsr.getDownloadState()); case NOT_STARTED: String heading = vsr.getHeading(); String highlightedHeading = highlightMatches(heading); LOG.debugf("Heading: {0} highlightedMatches: {1}", heading, highlightedHeading); String subheading = vsr.getSubHeading(); String highlightedSubheading = highlightMatches(subheading); LOG.debugf("Subheading: {0} highlightedMatches: {1}", subheading, highlightedSubheading); if (!isDifferentLength(heading, highlightedHeading) && !isDifferentLength(subheading, highlightedSubheading)) { PropertyMatch propertyMatch = getPropertyMatch(vsr); if (propertyMatch != null) { return newResult(HeadingSubHeadingAndMetadata, vsr, highlightedHeading, highlightedSubheading, propertyMatch); } } return newResult(HeadingAndSubheading, vsr, highlightedHeading, highlightedSubheading, null); default: throw new UnsupportedOperationException("Unhandled download state: " + vsr.getDownloadState()); } } private RowDisplayResultImpl newResult(RowDisplayConfig config, VisualSearchResult vsr, String heading, String subheading, PropertyMatch propertyMatch) { if (emptyOrNull(subheading)) { if (propertyMatch == null || (propertyMatch.getKey() == null || EMPTY_STRING.equals(propertyMatch.getKey()))) { config = HeadingOnly; } else { config = HeadingAndMetadata; } } if (subheading.length() > 0) { subheading = OPEN_HTML + subheading + CLOSE_HTML; } return new RowDisplayResultImpl(config, heading, propertyMatch, subheading, vsr.isSpam(), vsr.getDownloadState()); } private boolean emptyOrNull(String val) { return val == null || EMPTY_STRING.equals(val.trim()); } private PropertyMatch getPropertyMatch(VisualSearchResult vsr) { if(searchText == null) return null; List<FilePropertyKey> keys = null; switch(vsr.getCategory()) { case AUDIO: keys = audioKeyOrder; break; case DOCUMENT: keys = documentKeyOrder; break; case PROGRAM: keys = programKeyOrder; break; case VIDEO: keys = videoKeyOrder; break; } if(keys == null) { return null; } for (FilePropertyKey key : keys) { String value = vsr.getPropertyString(key); if (value != null) { String propertyMatch = highlightMatches(value); if (isDifferentLength(value, propertyMatch)) { String betterKey = key.toString().toLowerCase(); betterKey = betterKey.replace('_', ' '); return new PropertyMatchImpl(betterKey, propertyMatch); } } } // No match found. return null; } private boolean isDifferentLength(String str1, String str2) { return str1 != null && str2 != null && str1.length() != str2.length(); } /** * Adds an HTML bold tag around every occurrence of highlightText. * Note that comparisons are case insensitive. * @param text the text to be modified * @return the text containing bold tags */ private String highlightMatches(String sourceText) { boolean haveSearchText = searchText != null && searchText.length() > 0; // If there is no search or filter text then return sourceText as is. if (!haveSearchText) return sourceText; return searchHighlightUtil.highlight(sourceText); } private static class RowDisplayResultImpl implements RowDisplayResult { private final RowDisplayConfig config; private final String heading; private final String subheading; private final PropertyMatch metadata; private final boolean spam; private final BasicDownloadState initialDownloadState; public RowDisplayResultImpl(RowDisplayConfig config, String heading, PropertyMatch metadata, String subheading, boolean spam, BasicDownloadState downloadState) { this.config = config; this.heading = heading; this.metadata = metadata; this.subheading = subheading; this.spam = spam; this.initialDownloadState = downloadState; } @Override public RowDisplayConfig getConfig() { return config; } @Override public String getHeading() { return heading; } @Override public PropertyMatch getMetadata() { return metadata; } @Override public String getSubheading() { return subheading; } @Override public boolean isSpam() { return spam; } @Override public boolean isStale(VisualSearchResult vsr) { return vsr.getDownloadState() != initialDownloadState || vsr.isSpam() != spam; } } private static class PropertyMatchImpl implements PropertyMatch { private final String highlightedVal; private final String key; public PropertyMatchImpl(String key, String highlightedVal) { this.key = key; this.highlightedVal = highlightedVal; } @Override public String getHighlightedValue() { return highlightedVal; } @Override public String getKey() { return key; } } private static class PropertyKeyComparator implements Comparator<FilePropertyKey> { private final FilePropertyKey[] keyOrder; public PropertyKeyComparator(FilePropertyKey... keys) { this.keyOrder = keys; } @Override public int compare(FilePropertyKey o1, FilePropertyKey o2) { if (o1 == o2) { return 0; } for(FilePropertyKey key : keyOrder) { if (o1 == key) { return -1; } else if (o2 == key) { return 1; } } return 0; } } }