/* 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 group of Mart FilterDescriptions. * * @author <a href="mailto:dlondon@ebi.ac.uk">Darin London</a> * @author <a href="mailto:craig@ebi.ac.uk">Craig Melsopp</a> */ public class FilterCollection extends BaseNamedConfigurationObject { private boolean hasBrokenFilters = false; // FilterDescriptions private List filters = new ArrayList(); private Hashtable filterNameMap = new Hashtable(); //cache one FilterDescription for call to containsFilterDescription or getFiterDescriptionByInternalName private FilterDescription lastFilt = null; //cache one FilterDescription for call to supports private FilterDescription lastSupportFilt = null; protected final String enableSelectAllKey = "enableSelectAll"; private final String[] titles = new String[] { enableSelectAllKey }; /** * Copy Constructor. Constructs an exact copy of an existing * FilterCollection. * @param fc FilterCollection to copy. */ public FilterCollection(FilterCollection fc) { super(fc); List fds = fc.getFilterDescriptions(); for (int i = 0, n = fds.size(); i < n; i++) { Object fd = fds.get(i); if (fd instanceof FilterDescription) addFilterDescription( new FilterDescription( (FilterDescription) fd) ); //else not needed } } /** * Empty Constructor should only be used by DatasetConfigEditor. * */ public FilterCollection() { super(); for (int i = 0, n = titles.length; i < n; i++) { setAttribute(titles[i], null); //establishes the order of the keys, and adds all possible attribute titles to getXMLAttributeTitles, even if never set in future } } /** * Constructor for a FilterCollection named by intenalName * * @param internalName String name to internally represent the FilterCollection. Must not be null. * @throws ConfigurationException when paremeter requirements are not met */ public FilterCollection(String internalName) throws ConfigurationException { this(internalName, "", "", ""); } /** * Constructor for a FilterCollection named by intenalName, with a displayName, and description. * * @param internalName String name to internally represent the FilterCollection. Must not be null or empty. * @param displayName String name to represent the FilterCollection. * @param description String description of the FilterCollection. * @throws ConfigurationException when paremeters are null or empty */ public FilterCollection(String internalName, String displayName, String description, String enableSelectAll) throws ConfigurationException { super(internalName, displayName, description); setAttribute(enableSelectAllKey, enableSelectAll); } /** * Add a FilterDescription object to this FilterCollection. * * @param f a FilterDescription object */ public void addFilterDescription(FilterDescription f) { filters.add(f); filterNameMap.put(f.getInternalName(), f); } /** * Remove a FilterDescription from the FilterCollection. * @param f -- FilterDescription to remove. */ public void removeFilterDescription(FilterDescription f) { filterNameMap.remove(f.getInternalName()); filters.remove(f); // fix to stop containsFilterDescription breaking after a remove if (lastFilt != null && f.getInternalName().equals(lastFilt.getInternalName())) lastFilt = null; } /** * Insert a FilterDescription at a specific position within the List of FilterDescriptions within this FilterCollection. * FilterDescription Objects occuring at or after the given position will be shifted right. * @param position -- position at which to insert the FilterDescription * @param f -- FilterDescription to be inserted. */ public void insertFilterDescription(int position, FilterDescription f) { filters.add(position, f); filterNameMap.put(f.getInternalName(), f); } /** * Insert a FilterDescription before a specific FilterDescription, named by internalName. * @param internalName -- internalName of FilterDescription before which the given FilterDescription should be inserted. * @param f -- FilterDescription to insert. * @throws ConfigurationException when the FilterCollection does not contain a FilterDescription named by internalName. */ public void insertFilterDescriptionBeforeFilterDescription(String internalName, FilterDescription f) throws ConfigurationException { if (!filterNameMap.containsKey(internalName)) throw new ConfigurationException("FilterCollection does not contain FilterDescription " + internalName + "\n"); insertFilterDescription( filters.indexOf( filterNameMap.get(internalName) ), f ); } /** * Insert a FilterDescription after a specific FilterDescription, named by internalName. * @param internalName -- internalName of FilterDescription after which the given FilterDescription should be inserted. * @param f -- FilterDescription to insert. * @throws ConfigurationException when the FilterCollection does not contain a FilterDescription named by internalName. */ public void insertFilterDescriptionAfterFilterDescription(String internalName, FilterDescription f) throws ConfigurationException { if (!filterNameMap.containsKey(internalName)) throw new ConfigurationException("FilterCollection does not contain FilterDescription " + internalName + "\n"); insertFilterDescription( filters.indexOf( filterNameMap.get(internalName) ) + 1, f ); } /** * Add a group of FilterDescription objects in one call. * Note, subsequent calls to addFilterDescription and addFilterDescriptions will add to what has * been added previously. * * @param f an array of FilterDescription objects. */ public void addFilterDescriptions(FilterDescription[] f) { for (int i = 0, n = f.length; i < n; i++) { filters.add(f[i]); filterNameMap.put(f[i].getInternalName(), f[i]); } } /** * Returns a List of FilterDescription objects, * in the order they were added. * * @return List of FilterDescription objects */ public List getFilterDescriptions() { return new ArrayList(filters); } /** * Returns a specific FilterDescription, named by internalName, or * containing an Option named by internalName. * * @param internalName String name of the requested FilterDescription * @return FilterDescription requested, or null. */ public FilterDescription getFilterDescriptionByInternalName(String internalName) { if (containsFilterDescription(internalName)) return lastFilt; else return null; } /** * Check if this FilterCollection contains a specific FilterDescription object. * * @param internalName String name of the requested FilterDescription * @return boolean, true if FilterCollection contains the FilterDescription, false if not. */ public boolean containsFilterDescription(String internalName) { boolean contains = false; if (lastFilt == null) { contains = filterNameMap.containsKey(internalName); if (contains) lastFilt = (FilterDescription) filterNameMap.get(internalName); else if ( ( internalName.indexOf(".") > 0 ) && !( internalName.endsWith(".") ) ) { String[] testNames = internalName.split("\\."); String testRefName = testNames[0]; // x in x.y String testIname = testNames[1]; // y in x.y if (filterNameMap.containsKey(testIname)) { // y is an actual filter, with its values stored in a PushOption in another Filter lastFilt = (FilterDescription) filterNameMap.get(testIname); contains = true; } else { // y may be a Filter stored in a PushOption within another Filter for (Iterator iter = filters.iterator(); iter.hasNext();) { FilterDescription element = (FilterDescription) iter.next(); if (element.containsOption(testRefName)) { Option superOption = element.getOptionByInternalName(testRefName); PushAction[] pos = superOption.getPushActions(); for (int i = 0, n = pos.length; i < n; i++) { PushAction po = pos[i]; if (po.containsOption(testIname)) { lastFilt = element; contains = true; break; } } } if (contains) break; } } } else { for (Iterator iter = filters.iterator(); iter.hasNext();) { FilterDescription element = (FilterDescription) iter.next(); if (element.containsOption(internalName)) { lastFilt = element; //lastFilt = new FilterDescription(element.getOptionByInternalName(internalName)); contains = true; break; } } } } else {// lastFilt != null 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 for a given field and tableConstraint. Useful for mapping the field and tableConstraint from a Filter * object added to a Query object back to its MartConfiguration FilterDescription. * @param field -- String mart database field * @param tableConstraint -- String mart database tableConstraint * @param qualifier -- Filter qualifier * @return FilterDescription supporting the given field and tableConstraint, or null. */ public FilterDescription getFilterDescriptionByFieldNameTableConstraint(String field, String tableConstraint, String qualifier) { if (supports(field, tableConstraint, qualifier)) return lastSupportFilt; else return null; } /** * Determine if this FilterCollection contains a FilterDescription supporting a given field and tableConstraint. * @param field - String field of a mart database table * @param TableConstraint -- String tableConstraint of a mart database table * @param qualifier -- Filter qualifier * @return boolean, true if a FilterDescription contained within this collection supports the field and tableConstraint, false otherwise. */ public boolean supports(String field, String TableConstraint, String qualifier) { boolean supports = false; if (lastSupportFilt == null) { for (Iterator iter = filters.iterator(); iter.hasNext();) { Object element = iter.next(); if (element instanceof FilterDescription) { if (((FilterDescription) element).supports(field, TableConstraint,qualifier)) { lastSupportFilt = (FilterDescription) element; supports = true; break; } } } } else { if (lastSupportFilt.supports(field, TableConstraint, qualifier)) supports = true; else { lastSupportFilt = null; supports = supports(field, TableConstraint,qualifier); } } return supports; } /** * Returns a List of possible internalNames to add to the MartCompleter command completion system. * internalNames may be of the form x.y. Also, internalNames that are not of the form x.y, but are found to be equal to y in * another internalName of form x.y will not be added as potential completers. * @return List of potential completer names */ public List getCompleterNames() { List names = new ArrayList(); for (Iterator iter = filters.iterator(); iter.hasNext();) { FilterDescription element = (FilterDescription) iter.next(); if (element.getHidden() != null && element.getHidden().equals("true")) continue; if (element.getDisplay() != null && element.getDisplay().equals("true")) continue; names.addAll(element.getCompleterNames()); } 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); } public String toString() { StringBuffer buf = new StringBuffer(); buf.append("["); buf.append(super.toString()); buf.append(", FilterDescriptions=").append(filters); buf.append("]"); return buf.toString(); } /** * Allows Equality Comparisons manipulation of FilterCollection objects */ public boolean equals(Object o) { return o instanceof FilterCollection && hashCode() == o.hashCode(); } public int hashCode() { int hashcode = super.hashCode(); for (Iterator iter = filters.iterator(); iter.hasNext();) { Object element = iter.next(); hashcode = (31 * hashcode) + element.hashCode(); } return hashcode; } /** * set the hasBrokenFilters flag to true, meaning that one or more FilterDescription Objects (or their child Option Objects) * within this FilterCollection contain invalid field/tableConstraint references to a specific Mart instance. */ public void setFiltersBroken() { hasBrokenFilters = true; } /** * Determine if this FilterCollection has FilterDescriptions that are broken. * @return boolean */ public boolean hasBrokenFilters() { return hasBrokenFilters; } /** * True if hasBrokenFilters is true. * @return boolean */ public boolean isBroken() { return hasBrokenFilters; } public boolean containsOnlyPointerFilters() { boolean ret = true; List filters = getFilterDescriptions(); for (int i = 0, n = filters.size(); i < n; i++) { FilterDescription filt = (FilterDescription) filters.get(i); if (filt.getHidden() != null && filt.getHidden().equals("true")) continue; if (filt.getDisplay() != null && filt.getDisplay().equals("true")) continue; if (filt.getInternalName().indexOf('.') < 0) { ret = false; break; } } return ret; } public boolean containsOnlyFilterListFilterUploadFilters() { boolean ret = true; List filters = getFilterDescriptions(); for (int i = 0, n = filters.size(); i < n; i++) { FilterDescription filt = (FilterDescription) filters.get(i); if (filt.getFilterList() == null || filt.getFilterList().length() < 1) { ret = false; break; } } return ret; } public void setEnableSelectAll(String value) { setAttribute(enableSelectAllKey, value); } public String getEnableSelectAll() { return getAttribute(enableSelectAllKey); } }