FilterPipeline.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:19k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: FilterPipeline.java,v 1.14 2005/10/13 08:59:52 kleopatra Exp $
  3.  *
  4.  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.swingx.decorator;
  22. import java.util.BitSet;
  23. import java.util.List;
  24. import java.util.Vector;
  25. import javax.swing.event.EventListenerList;
  26. /**
  27.  * <p>A <b><code>FilterPipeline</code></b> is used to define the set of
  28.  * {@link org.jdesktop.swingx.decorator.Filter filters}
  29.  * for a data-aware component such as a {@link org.jdesktop.swingx.JXList} or a
  30.  * {@link org.jdesktop.swingx.JXTable}. Filtering involves interposing one or
  31.  * more filters in a {@link org.jdesktop.swingx.decorator.FilterPipeline} between
  32.  * a data model and a view to change the apparent order and/or number of records
  33.  * in the data model. The order of filters in the filter pipeline determines the
  34.  * order in which each filter is applied. The output from one filter in the
  35.  * pipeline is piped as the input to the next filter in the pipeline.</p>
  36.  *
  37.  * <pre>
  38.  *  {@link org.jdesktop.swingx.decorator.Filter}[]   filters = new {@link org.jdesktop.swingx.decorator.Filter}[] {
  39.  *      new {@link org.jdesktop.swingx.decorator.PatternFilter}("S.*", 0, 1),    // regex, matchflags, column
  40.  *      new {@link org.jdesktop.swingx.decorator.ShuttleSorter}(1, false),   // column 1, descending
  41.  *      new {@link org.jdesktop.swingx.decorator.ShuttleSorter}(0, true),    // column 0, ascending
  42.  *  };
  43.  *  {@link org.jdesktop.swingx.decorator.FilterPipeline} pipeline = new {@link org.jdesktop.swingx.decorator.FilterPipeline}(filters);
  44.  *  {@link org.jdesktop.swingx.JXTable}  table = new {@link org.jdesktop.swingx.JXTable}(model);
  45.  *  table.setFilters(pipeline);
  46.  * </pre>
  47.  *
  48.  * This is all you need to do in order to use <code>FilterPipeline</code>. Most
  49.  * of the methods in this class are only for advanced developers who want to write
  50.  * their own filter subclasses and want to override the way a filter pipeline works.
  51.  *
  52.  * @author Ramesh Gupta
  53.  * @see org.jdesktop.swingx.decorator.Filter
  54.  */
  55. public class FilterPipeline {
  56.     protected EventListenerList     listenerList = new EventListenerList();
  57.     private ComponentAdapter    adapter = null;
  58.     private Sorter                  sorter = null;
  59.     private final Filter[]          filters;
  60.     /**
  61.      * Creates an empty open pipeline.
  62.      *
  63.      */
  64.     public FilterPipeline() {
  65.         this(new Filter[] {});
  66.     }
  67.     /**
  68.      * Constructs a new <code>FilterPipeline</code> populated with the specified
  69.      * filters that are applied in the order they appear in the list. Since filters
  70.      * maintain state about the view to which they are attached, an instance of
  71.      * a filter may not ever be used in more than one pipeline.
  72.      *
  73.      * @param inList array of filters
  74.      */
  75.     public FilterPipeline(Filter[] inList) {
  76.         filters = reorderSorters(inList, locateSorters(inList));
  77.         assignFilters();
  78.     }
  79.     /*
  80.      * JW let each contained filter assign both order and pipeline.
  81.      * Now we have a invariant 
  82.      * 
  83.      * (containedFilter.order >= 0) && (containedFilter.pipeline != null)
  84.      * 
  85.      * which simplifies access logic (IMO)
  86.      *
  87.      */
  88.     private void assignFilters() {
  89.         for (int i = 0; i < filters.length; i++) {
  90.             // JW: changed to bind early and move 
  91.             // binding responsibility to filter
  92.             // instead of fiddling around in other's bowels
  93.             filters[i].assign(this, i);
  94.         }
  95.     }
  96.     /**
  97.      * Sets the sorter that the output of the filter pipeline is piped through.
  98.      * This is the sorter that is installed interactively on a view by a user
  99.      * action. 
  100.      *
  101.      * This method is responsible for doing all the bookkeeping to assign/cleanup
  102.      * pipeline/adapter assignments. 
  103.      * 
  104.      * @param sorter the interactive sorter, if any; null otherwise.
  105.      */
  106.     public void setSorter(Sorter sorter) {
  107.         Sorter oldSorter = getSorter();
  108.         if (oldSorter == sorter) return;
  109.         if (oldSorter != null) {
  110.             oldSorter.assign((FilterPipeline) null);
  111.         }
  112.         this.sorter = sorter;
  113.         if (sorter != null) {
  114.             sorter.assign((FilterPipeline) null);
  115.             sorter.assign(this);
  116.             if (adapter != null) {
  117.                 sorter.assign(adapter);
  118.                 sorter.refresh();
  119.             }
  120.         } 
  121.         if ((sorter == null) && isAssigned()) { 
  122.             fireContentsChanged();
  123.         }
  124.     }
  125.     /**
  126.      * returns the interactive sorter or null if none is set.
  127.      * 
  128.      * @return
  129.      */
  130.     public Sorter getSorter() {
  131.         return sorter;
  132.     }
  133.     
  134.     /**
  135.      * Assigns a {@link org.jdesktop.swingx.decorator.ComponentAdapter} to this
  136.      * pipeline if no adapter has previously been assigned to the pipeline. Once an
  137.      * adapter has been assigned to this pipeline, any attempt to change that will
  138.      * cause an exception to be thrown.
  139.      *
  140.      * @param adapter the <code>ComponentAdapter</code> to assign
  141.      * @throws IllegalArgumentException if adapter is null
  142.      * @throws IllegalStateException if an adapter is already assigned to this
  143.      * pipeline and the new adapter is not the same the existing adapter
  144.      */
  145.      public final void assign(ComponentAdapter adapter) {
  146.         if (adapter == null) {
  147.             throw new IllegalArgumentException("null adapter");
  148.         }
  149.     // also assign individual filters when adapter is bound
  150.         if (this.adapter == null) {
  151.             this.adapter = adapter;
  152.             for (int i = 0; i < filters.length; i++) {
  153.                 filters[i].assign(adapter);
  154.             }
  155.             if (sorter != null) {
  156.                 sorter.assign(adapter);
  157.             }
  158.             flush();
  159.         }
  160.         else if (this.adapter != adapter){
  161.             throw new IllegalStateException("Can't bind to a different adapter");
  162.         }
  163.     }
  164.      /**
  165.       * 
  166.       * @return
  167.       */
  168.      public boolean isAssigned() {
  169.          return adapter != null;
  170.      }
  171.     /**
  172.      * Returns true if this pipeline contains the specified filter;
  173.      * otherwise it returns false.
  174.      *
  175.      * @param filter filter whose membership in this pipeline is tested
  176.      * @return true if this pipeline contains the specified filter;
  177.      * otherwise it returns false
  178.      * @throws NullPointerException if filter == null
  179.      * 
  180.      */
  181.     boolean contains(Filter filter) {
  182.         return (filter.equals(sorter)) || 
  183.             (filter.order >= 0) &&
  184.                 (filters.length > 0) && 
  185.                 (filters[filter.order] == filter);
  186.     }
  187.     /**
  188.      * Returns the first filter, if any, in this pipeline, or null, if there are
  189.      * no filters in this pipeline.
  190.      *
  191.      * @return the first filter, if any, in this pipeline, or null, if there are
  192.      * no filters in this pipeline
  193.      */
  194.     Filter first() {
  195.         return (filters.length > 0) ? filters[0] : null;
  196.     }
  197.     /**
  198.      * Returns the last filter, if any, in this pipeline, or null, if there are
  199.      * no filters in this pipeline.
  200.      *
  201.      * @return the last filter, if any, in this pipeline, or null, if there are
  202.      * no filters in this pipeline
  203.      */
  204.     Filter last() {
  205.         if (sorter != null) return sorter;
  206.         return (filters.length > 0) ? filters[filters.length - 1] : null;
  207.     }
  208.     /**
  209.      * Returns the filter after the supplied filter in this pipeline, or null,
  210.      * if there aren't any filters after the supplied filter.
  211.      * 
  212.      * @param filter a filter in this pipeline
  213.      * @return the filter after the supplied filter in this pipeline, or null,
  214.      *         if there aren't any filters after the supplied filter
  215.      */
  216.     Filter next(Filter filter) {
  217.         if (last().equals(filter))
  218.             return null;
  219.         return filter.order + 1 < filters.length ? filters[filter.order + 1]
  220.                 : getSorter();
  221.     }
  222.     /**
  223.      * Returns the filter before the supplied filter in this pipeline,
  224.      * or null, if there aren't any filters before the supplied filter.
  225.      *
  226.      * @param filter a filter in this pipeline
  227.      * @return the filter before the supplied filter in this pipeline,
  228.      * or null, if there aren't any filters before the supplied filter
  229.      */
  230.     Filter previous(Filter filter) {
  231.         if (filter.equals(sorter)) return filters.length > 0 ? filters[filters.length - 1] : null;
  232.         return first().equals(filter) ? null : filters[filter.order - 1];
  233.     }
  234.     /**
  235.      * Called when the specified filter has changed.
  236.      * Cascades <b><code>filterChanged</code></b> notifications to the next
  237.      * filter in the pipeline after the specified filter. If the specified filter
  238.      * is the last filter in the pipeline, this method broadcasts a
  239.      * <b><code>filterChanged</code></b> notification to all
  240.      * <b><code>PipelineListener</code></b> objects registered with this pipeline.
  241.      *
  242.      * @param filter a filter in this pipeline that has changed in any way
  243.      */
  244.     protected void filterChanged(Filter filter) {
  245.         Filter  next = next(filter); 
  246.         if (next == null) {
  247.             fireContentsChanged();
  248.         }
  249.         else {
  250.             next.refresh(); // Cascade to next filter
  251.         }
  252.     }
  253.     /**
  254.      * returns the unfiltered data adapter size or 0 if unassigned.
  255.      * 
  256.      * @return
  257.      */
  258.     public int getInputSize() {
  259.         return isAssigned() ? adapter.getRowCount() : 0;
  260.     }
  261.     /**
  262.      * @param filter
  263.      * @return
  264.      */
  265.     int getInputSize(Filter filter) {
  266.         Filter  previous = previous(filter);
  267.         if (previous != null) {
  268.             return previous.getSize();
  269.         }
  270.         // fixed issue #64-swingx - removed precondition... (was: isAssigned())
  271.         return getInputSize();
  272.     }
  273.     /**
  274.      * Returns the number of records in the filtered view.
  275.      *
  276.      * @return the number of records in the filtered view
  277.      */
  278.     public int getOutputSize() {
  279.         // JW: don't need to check - but that's heavily dependent on the
  280.         // implementation detail that there's always the identityFilter
  281.         // (which might change any time)
  282.         if (!isAssigned()) return 0;
  283.         Filter last = last();
  284.         return (last == null) ? adapter.getRowCount() : last.getSize();
  285.     }
  286.     /**
  287.      * Convert row index from view coordinates to model coordinates
  288.      * accounting for the presence of sorters and filters. This is essentially
  289.      * a pass-through to the {@link org.jdesktop.swingx.decorator.Filter#convertRowIndexToModel(int) convertRowIndexToModel}
  290.      * method of the <em>last</em> {@link org.jdesktop.swingx.decorator.Filter},
  291.      * if any, in this pipeline.
  292.      *
  293.      * @param row row index in view coordinates
  294.      * @return row index in model coordinates
  295.      */
  296.     public int convertRowIndexToModel(int row) {
  297.         Filter last = last();
  298.         return (last == null) ? row : last.convertRowIndexToModel(row);
  299.     }
  300.     /**
  301.      * Convert row index from model coordinates to view coordinates
  302.      * accounting for the presence of sorters and filters. This is essentially
  303.      * a pass-through to the {@link org.jdesktop.swingx.decorator.Filter#convertRowIndexToView(int) convertRowIndexToModel}
  304.      * method of the <em>last</em> {@link org.jdesktop.swingx.decorator.Filter},
  305.      * if any, in this pipeline.
  306.      *
  307.      * @param row row index in model coordinates
  308.      * @return row index in view coordinates
  309.      */
  310.     public int convertRowIndexToView(int row) {
  311.         Filter last = last();
  312.         return (last == null) ? row : last.convertRowIndexToView(row);
  313.     }
  314.     /**
  315.      * Returns the value of the cell at the specified coordinates.
  316.      *
  317.      * 
  318.      * @param row in view coordinates
  319.      * @param column in model coordinates
  320.      * @return the value of the cell at the specified coordinates
  321.      */
  322.     public Object getValueAt(int row, int column) {
  323.         // JW: this impl relies on the fact that there's always the
  324.         // identity filter installed
  325.         // should use adapter if assigned and no filter
  326.         Filter last = last();
  327.         return (last == null) ? null : last.getValueAt(row, column);
  328.     }
  329.     public void setValueAt(Object aValue, int row, int column) {
  330.         // JW: this impl relies on the fact that there's always the
  331.         // identity filter installed
  332.         // should use adapter if assigned and no filter
  333.         Filter last = last();
  334.         if (last != null) {
  335.             last.setValueAt(aValue, row, column);
  336.         }
  337.     }
  338.     public boolean isCellEditable(int row, int column) {
  339.         // JW: this impl relies on the fact that there's always the
  340.         // identity filter installed
  341.         // should use adapter if assigned and no filter
  342.         Filter last = last();
  343.         return (last == null) ? false : last.isCellEditable(row, column);
  344.     }
  345.     /**
  346.      * Flushes the pipeline by initiating a {@link org.jdesktop.swingx.decorator.Filter#refresh() refresh}
  347.      * on the <em>first</em> {@link org.jdesktop.swingx.decorator.Filter filter},
  348.      * if any, in this pipeline. After that filter has refreshed itself, it sends a
  349.      * {@link #filterChanged(org.jdesktop.swingx.decorator.Filter) filterChanged}
  350.      * notification to this pipeline, and the pipeline responds by initiating a
  351.      * {@link org.jdesktop.swingx.decorator.Filter#refresh() refresh}
  352.      * on the <em>next</em> {@link org.jdesktop.swingx.decorator.Filter filter},
  353.      * if any, in this pipeline. Eventualy, when there are no more filters left
  354.      * in the pipeline, it broadcasts a {@link org.jdesktop.swingx.decorator.PipelineEvent}
  355.      * signaling a {@link org.jdesktop.swingx.decorator.PipelineEvent#CONTENTS_CHANGED}
  356.      * message to all {@link org.jdesktop.swingx.decorator.PipelineListener} objects
  357.      * registered with this pipeline.
  358.      */
  359.     public void flush() {
  360.         // JW PENDING: use first!
  361.         if ((filters != null) && (filters.length > 0)) {
  362.             filters[0].refresh();
  363.         }
  364.         else if (sorter != null) {
  365.             sorter.refresh();
  366.         }
  367.     }
  368.     /**
  369.      * Adds a listener to the list that's notified each time there is a change
  370.      * to this pipeline.
  371.      *
  372.      * @param l the <code>PipelineListener</code> to be added
  373.      */
  374.     public void addPipelineListener(PipelineListener l) {
  375.         listenerList.add(PipelineListener.class, l);
  376.     }
  377.     /**
  378.      * Removes a listener from the list that's notified each time there is a change
  379.      * to this pipeline.
  380.      *
  381.      * @param l the <code>PipelineListener</code> to be removed
  382.      */
  383.     public void removePipelineListener(PipelineListener l) {
  384.         listenerList.remove(PipelineListener.class, l);
  385.     }
  386.     /**
  387.      * Returns an array of all the pipeline listeners
  388.      * registered on this <code>FilterPipeline</code>.
  389.      *
  390.      * @return all of this pipeline's <code>PipelineListener</code>s,
  391.      *         or an empty array if no pipeline listeners
  392.      *         are currently registered
  393.      *
  394.      * @see #addPipelineListener
  395.      * @see #removePipelineListener
  396.      */
  397.     public PipelineListener[] getPipelineListeners() {
  398.         return (PipelineListener[]) listenerList.getListeners(
  399.             PipelineListener.class);
  400.     }
  401.     /**
  402.      * Notifies all registered {@link org.jdesktop.swingx.decorator.PipelineListener}
  403.      * objects that the contents of this pipeline has changed. The event instance
  404.      * is lazily created.
  405.      */
  406.     protected void fireContentsChanged() {
  407.         Object[] listeners = listenerList.getListenerList();
  408.         PipelineEvent e = null;
  409.         for (int i = listeners.length - 2; i >= 0; i -= 2) {
  410.             if (listeners[i] == PipelineListener.class) {
  411.                 if (e == null) {
  412.                     e = new PipelineEvent(this, PipelineEvent.CONTENTS_CHANGED);
  413.                 }
  414.                 ( (PipelineListener) listeners[i + 1]).contentsChanged(e);
  415.             }
  416.         }
  417.     }
  418.     private List locateSorters(Filter[] inList) {
  419.         BitSet  sortableColumns = new BitSet(); // temporary structure for checking
  420.         List    sorterLocations = new Vector();
  421.         for (int i = 0; i < inList.length; i++) {
  422.             if (inList[i] instanceof Sorter) {
  423.                 int columnIndex = inList[i].getColumnIndex();
  424.                 if (columnIndex < 0) {
  425.                     throw new IndexOutOfBoundsException(
  426.                         "Negative column index for filter: " + inList[i]);
  427.                 }
  428.                 if (sortableColumns.get(columnIndex)) {
  429.                     throw new IllegalArgumentException(
  430.                         "Filter "+ i +" attempting to overwrite sorter for column "
  431.                         + columnIndex);
  432.                 }
  433.                 sortableColumns.set(columnIndex);       // mark column index
  434.                 sorterLocations.add(new Integer(i));    // mark sorter index
  435.                 //columnSorterMap.put(new Integer(columnIndex), inList[i]);
  436.             }
  437.         }
  438.         return sorterLocations;
  439.     }
  440.     private Filter[] reorderSorters(Filter[] inList, List sorterLocations) {
  441.         // quick hack for issue #46-swingx: make sure we are open
  442.         // without filter.
  443.         if (inList.length == 0) {
  444.             return new Filter[] {new IdentityFilter()};
  445.         }
  446.         // always returns a new copy of inList
  447.         Filter[]    outList = (Filter[]) inList.clone();
  448.         // Invert the order of sorters, if any, in outList
  449.         int max = sorterLocations.size() - 1;
  450.         for (int i = 0; i <= max; i++) {
  451.             int orig = ((Integer) sorterLocations.get(max - i)).intValue();
  452.             int copy = ((Integer) sorterLocations.get(i)).intValue();
  453.             outList[copy] = inList[orig];
  454.         }
  455.         return outList;
  456.     }
  457.     public class IdentityFilter extends Filter {
  458.         protected void init() {
  459.         }
  460.         protected void reset() {
  461.         }
  462.         protected void filter() {
  463.         }
  464.         public int getSize() {
  465.             return getInputSize();
  466.         }
  467.         protected int mapTowardModel(int row) {
  468.             return row;
  469.         }
  470.         protected int mapTowardView(int row) {
  471.             return row;
  472.         }
  473.     }
  474. }