/* Copyright (C) 2003 EBI, GRL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.ensembl.mart.lib.config; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; /** * Container for a set of Mart FilterCollections. * * @author <a href="mailto:dlondon@ebi.ac.uk">Darin London</a> * @author <a href="mailto:craig@ebi.ac.uk">Craig Melsopp</a> */ public class FilterPage extends BaseNamedConfigurationObject { private boolean hasBrokenGroups = false; private List filterGroups = new ArrayList(); private Hashtable filterGroupNameMap = new Hashtable(); //cache one FilterDescription Object for call to containsUIFilterDescription or getUIFiterDescriptionByName private FilterDescription lastFilt = null; //cache one FilterGroup for call to getGroupForFilter private FilterGroup lastGroup = null; //cache one FilterCollection for call to getCollectionForFilter private FilterCollection lastColl = null; //cache one FilterDescription for call to supports/getFilterDescriptionByFieldNameTableConstraint private FilterDescription lastSupportingFilter = null; /** * Copy constructor. Creates an exact copy of an existing FilterPage. * @param fp FilterPage to copy */ public FilterPage(FilterPage fp) { super(fp); List groups = fp.getFilterGroups(); for (int i = 0, n = groups.size(); i < n; i++) { Object group = groups.get(i); if (group instanceof FilterGroup) addFilterGroup( new FilterGroup( (FilterGroup) group ) ); } } /** * Empty Constructor should only be used by DatasetConfigEditory */ public FilterPage() { super(); } /** * Constructs a FilterPage object named by internalName. * * @param internalName String name to internally represent the FilterPage */ public FilterPage(String internalName) throws ConfigurationException { this(internalName, "", ""); } /** * Constructs a FilterPage object named by internalName, with a displayName, and a description. * * @param internalName String name to internally represent the FilterPage. Must not be null. * @param displayName String name to represent the FilterPage * @param description String description of the FilterPage * @throws ConfigurationException when the internalName is null or empty */ public FilterPage(String internalName, String displayName, String description) throws ConfigurationException { super(internalName, displayName, description); } /** * Adds a FilterGroup to the FilterPage. * * @param fg a FilterGroup object. */ public void addFilterGroup(FilterGroup fg) { filterGroups.add(fg); filterGroupNameMap.put(fg.getInternalName(), fg); } /** * Remove a FilterGroup from this FilterPage. * @param fg -- FilterGroup to be removed. */ public void removeFilterGroup(FilterGroup fg) { filterGroupNameMap.remove(fg.getInternalName()); filterGroups.remove(fg); } /** * Insert a FilterGroup at a particular position within the list of FilterGroups/DSFilterGroups. * FilterGroup/DSFilterGroup Objects occurring at or after the given position are shifted right. * @param position -- position at which to insert the FilterGroup * @param fg -- FilterGroup to insert */ public void insertFilterGroup(int position, FilterGroup fg) { filterGroups.add(position, fg); filterGroupNameMap.put(fg.getInternalName(), fg); } /** * Insert a FilterGroup before a specific FilterGroup/DSFilterGroup, named by internalName. * @param internalName -- String internalName of FilterGroup/DSFilterGroup before which the given FilterGroup should be inserted. * @param fg -- FilterGroup to insert. * @throws ConfigurationException when the FilterPage does not contain a FilterGroup named by internalName. */ public void insertFilterGroupBeforeFilterGroup(String internalName, FilterGroup fg) throws ConfigurationException { if (!filterGroupNameMap.containsKey(internalName)) throw new ConfigurationException("FilterPage does not contain FilterGroup " + internalName + "\n" ); insertFilterGroup( filterGroups.indexOf( filterGroupNameMap.get(internalName) ), fg ); } /** * Insert a FilterGroup after a specific FilterGroup, named by internalName. * @param internalName -- String internalName of FilterGroup/DSFilterGroup after which the given FilterGroup should be inserted. * @param fg -- FilterGroup to insert. * @throws ConfigurationException when the FilterPage does not contain a FilterGroup named by internalName. */ public void insertFilterGroupAfterFilterGroup(String internalName, FilterGroup fg) throws ConfigurationException { if (!filterGroupNameMap.containsKey(internalName)) throw new ConfigurationException("FilterPage does not contain FilterGroup " + internalName + "\n" ); insertFilterGroup( filterGroups.indexOf( filterGroupNameMap.get(internalName) ) + 1, fg ); } /** * Adds a group of FilterGroup objects in one call. * Note, subsequent calls to addFilterGroup/DSFilterGroup, insertFilterGroup/DSFilterGroup, and addFilterGroups/DSFilterGroups will add to what * has already been aded. * * @param fg An Array of FilterGroup objects. */ public void addFilterGroups(FilterGroup[] fg) { for (int i = 0, n = fg.length; i < n; i++) { filterGroups.add(fg[i]); filterGroupNameMap.put(fg[i].getInternalName(), fg[i]); } } /** * Returns a List of FilterGroup/DSFilterGroup objects, in the order they were added. * * @return List of FilterGroup/DSFilterGroup objects. */ public List getFilterGroups() { return new ArrayList(filterGroups); } /** * Returns a particular FilterGroup object, named by the given internalName. * * @param internalName String name of the FilterGroup * @return Object (either FilterGroup or DSFilterGroup), or null. */ public Object getFilterGroupByName(String internalName) { if (filterGroupNameMap.containsKey(internalName)) return filterGroupNameMap.get(internalName); else return null; } /** * Check whether the FilterPage contains a given FilterGroup. * * @param internalName String name of the given FilterGroup * @return boolean, true if FilterPage contains the FilterGroup, false if not */ public boolean containsFilterGroup(String internalName) { return filterGroupNameMap.containsKey(internalName); } /** * Convenience method for non graphical UI. Allows a call against the FilterPage for a particular FilterDescription. * Note, it is best to first call containsFilterDescription, * as there is a caching system to cache a FilterDescription during a call to containsFilterDescription. * * @param internalName name of the requested FilterDescription * @return FilterDescription object, or null. */ public FilterDescription getFilterDescriptionByInternalName(String internalName) { if (containsFilterDescription(internalName)) return lastFilt; else return null; } /** * Convenience method for non graphical UI. Can determine if the FilterPage contains a specific FilterDescription. * As an optimization for initial calls to containsFilterDescription with an immediate call to getFilterDescriptionByInternalName, * this method caches the first FilterDescription it has found matching internalName. * * @param internalName name of the requested FilterDescription object * @return boolean, true if found, false if not. */ public boolean containsFilterDescription(String internalName) { boolean contains = false; if (lastFilt == null) { if ( ( internalName.indexOf(".") > 0 ) && !( internalName.endsWith(".") ) ) { String[] refs = internalName.split("\\."); if (refs.length > 1 && containsFilterDescription(refs[1])) contains = true; } if (!contains) { for (Iterator iter = (Iterator) filterGroups.iterator(); iter.hasNext();) { Object group = iter.next(); if (group instanceof FilterGroup && ((FilterGroup) group).containsFilterDescription(internalName)) { lastFilt = ((FilterGroup) group).getFilterDescriptionByInternalName(internalName); contains = true; break; } } } } else { if (lastFilt.getInternalName().equals(internalName)) contains = true; else if (lastFilt.containsOption(internalName)) contains = true; else if ( ( internalName.indexOf(".") > 0 ) && !(internalName.endsWith(".")) && lastFilt.getInternalName().equals(internalName.split("\\.")[1])) contains = true; else if (lastFilt.getInternalName().matches("\\w+\\." + internalName)){ contains = true; internalName = lastFilt.getInternalName(); } else { lastFilt = null; contains = containsFilterDescription(internalName); } } return contains; } /** * Get a FilterDescription object that supports a given field and tableConstraint. Useful for mapping from a Filter object * added to a Query back to its FilterDescription. * @param field -- String field of a mart database table * @param tableConstraint -- String tableConstraint of a mart database * @param qualifier -- Filter qualifier * @return FilterDescription object supporting the given field and tableConstraint, or null. */ public FilterDescription getFilterDescriptionByFieldNameTableConstraint(String field, String tableConstraint, String qualifier) { if (supports(field, tableConstraint, qualifier)) return lastSupportingFilter; else return null; } /** * Determine if this FilterPage contains a FilterDescription that supports a given field and tableConstraint. * Calling this method will cache any FilterDescription that supports the field and tableConstraint, and this will * be returned by a getFilterDescriptionByFieldNameTableConstraint call. * @param field -- String field of a mart database table * @param tableConstraint -- String tableConstraint of a mart database * @param qualifier -- Filter qualifier * @return boolean, true if the FilterPage contains a FilterDescription supporting a given field, tableConstraint, false otherwise. */ public boolean supports(String field, String tableConstraint, String qualifier) { boolean supports = false; if (lastSupportingFilter == null) { for (Iterator iter = filterGroups.iterator(); iter.hasNext();) { Object element = iter.next(); if (element instanceof FilterGroup) { FilterGroup fgroup = (FilterGroup) element; if (fgroup.supports(field, tableConstraint, qualifier)) { lastSupportingFilter = fgroup.getFilterDescriptionByFieldNameTableConstraint(field, tableConstraint,qualifier); supports = true; break; } } } } else { if (lastSupportingFilter.supports(field, tableConstraint, qualifier)) supports = true; else { lastSupportingFilter = null; supports = supports(field, tableConstraint,qualifier); } } return supports; } /** * Convenience Method to get all FilterDescription objects * in all Groups/Collections within a FilterPage. * * @return List of FilterDescription objects */ public List getAllFilterDescriptions() { List filts = new ArrayList(); for (Iterator iter = filterGroups.iterator(); iter.hasNext();) { Object fg = iter.next(); if (fg instanceof FilterGroup) filts.addAll(((FilterGroup) fg).getAllFilterDescriptions()); } return filts; } /** * Returns a FilterGroup for a particular Filter Description (FilterDescription or MapFilterDescription) * based on its internalName. * * @param internalName - String internalname for which a group is requested * @return FilterGroup object containing Filter Description with given internalName, or null. */ public FilterGroup getGroupForFilter(String internalName) { if (!containsFilterDescription(internalName)) return null; else if (lastGroup == null) { for (Iterator iter = filterGroups.iterator(); iter.hasNext();) { Object groupo = iter.next(); if (groupo instanceof FilterGroup) { FilterGroup group = (FilterGroup) groupo; if (group.containsFilterDescription(internalName)) { lastGroup = group; break; } } } return lastGroup; } else { if (lastGroup.getInternalName().equals(internalName)) return lastGroup; else { lastGroup = null; return getGroupForFilter(internalName); } } } /** * Returns a FilterCollection for a particular Filter Description (FilterDescription or MapFilterDescription) * based on its internalName. * * @param internalName - String internalname for which a collection is requested * @return FilterCollection object containing Filter Description with given internalName, or null. */ public FilterCollection getCollectionForFilter(String internalName) { if (!containsFilterDescription(internalName)) return null; else if (lastColl == null) { lastColl = getGroupForFilter(internalName).getCollectionForFilter(internalName); return lastColl; } else { if (lastColl.getInternalName().equals(internalName)) return lastColl; else { lastColl = null; return getCollectionForFilter(internalName); } } } /** * Retruns a List of possible Completion names for filters to the MartCompleter command completion system. * @return List possible completions */ public List getCompleterNames() { List names = new ArrayList(); for (Iterator iter = filterGroups.iterator(); iter.hasNext();) { Object group = iter.next(); if (( (BaseNamedConfigurationObject) group).getHidden() != null && ( (BaseNamedConfigurationObject) group).getHidden().equals("true")) continue; if (( (BaseNamedConfigurationObject) group).getDisplay() != null && ( (BaseNamedConfigurationObject) group).getDisplay().equals("true")) continue; if (group instanceof FilterGroup) { List thisNames = ((FilterGroup) group).getCompleterNames(); for (int i = 0, n = thisNames.size(); i < n; i++) { String completer = (String) thisNames.get(i); boolean addit = true; //look for completer, or x.completer for (int j = 0, m = names.size(); j < m; j++) { String check = (String) names.get(j); if (check.equals(completer) || (check.endsWith("." + completer))) { addit = false; break; } else if (completer.endsWith("." + check)) names.remove(check); } if (addit) names.add(completer); } } } return names; } /** * Allows MartShell to get all values associated with a given internalName (which may be of form x.y). * Behaves differently than getFilterDescriptionByInternalName when internalName is x.y and y is the name of * an actual filterDescription. * @param internalName * @return List of values to complete */ public List getCompleterValuesByInternalName(String internalName) { if (internalName.indexOf(".") > 0) return getFilterDescriptionByInternalName(internalName.split("\\.")[0]).getCompleterValues(internalName); else return getFilterDescriptionByInternalName(internalName).getCompleterValues(internalName); } /** * Allows MartShell to get all qualifiers associated with a given internalName (which may be of form x.y). * Behaves differently than getFilterDescriptionByInternalName when internalName is x.y and y is the name of * an actual filterDescription. * @param internalName * @return List of qualifiers to complete */ public List getFilterCompleterQualifiersByInternalName(String internalName) { if (internalName.indexOf(".") > 0 && !(internalName.endsWith(".")) && containsFilterDescription( internalName.split("\\.")[1] ) ) { String refname = internalName.split("\\.")[1]; return getFilterDescriptionByInternalName(refname).getCompleterQualifiers(refname); } else return getFilterDescriptionByInternalName(internalName).getCompleterQualifiers(internalName); } /** * debug output */ public String toString() { StringBuffer buf = new StringBuffer(); buf.append("["); buf.append(super.toString()); buf.append(", FilterGroups=").append(filterGroups); buf.append("]"); return buf.toString(); } /** * Allows Equality Comparisons manipulation of FilterPage objects */ public boolean equals(Object o) { return o instanceof FilterPage && hashCode() == o.hashCode(); } public int hashCode() { int tmp = super.hashCode(); for (Iterator iter = filterGroups.iterator(); iter.hasNext();) { Object element = iter.next(); tmp = (31 * tmp) + ((FilterGroup) element).hashCode(); } return tmp; } /** * Sets the hasBrokenGroups flag to true, meaning the FilterPage * contains one or more FilterGroup objects with broken FilterDescription objects. */ public void setGroupsBroken() { hasBrokenGroups = true; } /** * Determine if this FilterPage contains broken FilterGroups * @return boolean, true if one ore more FilterGroups is Broken, false otherwise */ public boolean hasBrokenGroups() { return hasBrokenGroups; } /** * True if hasBrokenGroups is true * @return boolean */ public boolean isBroken() { return hasBrokenGroups; } }