Skip to Content
In order to sort a column of a Web Dynpro table, a separate Java class can be used for sorting the context elements. In this scenario, there is already a special class called TableSorter available in SDN which provides this function. However, there is no special class to implement filter function on a column of a table.   In my search for this specific function, I came across Mr. Bharathwaj R’s webblog on Enhancing Tables in WebDynpro Java – Custom built Table Utilities that explains wonderful features such as automatic total calculation for particular columns in a table, auto download to excel file, printing the table and generating analytical diagram of the table. But I wanted table filter function. Then I came across Mr.Subramanian Venkateswaran’s blog on Filtering Table Values using WebDynpro It was very useful in understanding how a filter works in a table. But I was looking for a generic method that implements filter function on any table. Instead of searching more, I thought lets try on my own. And here I am with a web blog on it. I came up with a TableFilter java class file similar to the tablesorter class file. You can download it from TableFilter.java You have to include this java file in your Web Dynpro project source directory to make use of it. Once you have done that, please mention your Web Dynpro project package name in this file.image Lets assume that you have a well-functioning table implemented in your web dynpro application and you want to have filter on a few columns of this table. image Suppose this table has data source as value node ‘vnTableDataSource’ which is a copy of value node ‘vnOriginalDataSource’. It means both the nodes contain same number of elements and have same attributes ‘desc’, ‘dueDate’, ‘itemNo’ and so on . These attributes are the columns of the table.  image Now value node ‘vnFilters’ having cardinality 1..1 contains attributes ‘desc’, ‘itemNo’ and ‘poNo’ that are bound to the filterValue property of respective table columns. That means these columns have been made filterable. image Now, context root attribute ‘TableFilter’ is created. It is of the type com.sap.tableproject.TableFilter   In order to trigger the filtering of columns, create an Action ‘Filter’ in the view.  Now, in the wdDoModifyView, enter the following code.if ( firstTime )      {           if ( null != view )            {                     IWDTable table = (IWDTable) view.getElement("Table");                wdContext.currentContextElement().setTableFilter(new TableFilter(table,wdThis.wdGetFilterAction()));           }      } And in the method ‘onActionFilter’ of the view, enter the following line of code     wdContext.currentContextElement().getTableFilter().filter(wdEvent,wdContext.nodeVnTableDataSource(),               wdContext.nodeVnOriginalDataSource(),               wdContext.nodeVnFilters()); Thats all. The filter has been implemented on your table. Now build your Web Dynpro project, deploy and run it. image On running the application, your table will look as shown in above image. Enter your filter value and hit enter key. You can also use regular expressions to filter the records. image
To report this post you need to login first.

13 Comments

You must be Logged on to comment or reply to a post.

  1. Valery Silaev
    Kapil,

    Really cool and usefull utility!

    Also my applauds to your coding style (just everything — naming conventions, documenting methods, methods/variables naming). As one friend of my says “this is disertation-quality code” 🙂

    Now the challenge — is it possible to combine TableSorter and TableFilter in single utility. Looks like they can’t be used simualtenously…

    VS

    (0) 
    1. Kapil Kamble Post author
      Hello Manuel
      I tutorial by Peter Vignet mentioned by you is much much better than that done by me. I ll suggest you to follow it.
      If still, you want the tablefilter java class file, please provide me your email id, i ll forward you the file.
      Regards
      Kapil
      (0) 
      1. Ashok D
        Hi Kapil,

        First of all thanks for this utility.

        I am using the tablefilter provided by you and I had a problem with the following case .Have u encountered such problem .Can you tell me the work aroungd for this.Its kind of urgent.Thanks.

        When the filter value is entered as ‘{‘ or ‘(‘ or ‘]’ the following exception is thrown.

        The initial exception that caused the request to fail, was:

           java.util.regex.PatternSyntaxException: Unclosed group near index 5 .*(.* ^

            at java.util.regex.Pattern.error(Pattern.java:1541)
            at java.util.regex.Pattern.accept(Pattern.java:1399)
            at java.util.regex.Pattern.group0(Pattern.java:2313)
            at java.util.regex.Pattern.sequence(Pattern.java:1586)
            at java.util.regex.Pattern.expr(Pattern.java:1558)
            … 34 more

        Same exception for characters ‘{‘ or ‘[‘ or ‘(‘

        Many Thanks,
        Ashok D

        (0) 
        1. Kapil Kamble Post author
          Hi Ashok
          One way to avoid your exception is by escaping the character, by that I mean is give a filter value “\\{” if you want to look-up for “{” character.
          The other way will be to change the java class file. In the method “refineFilterString ” of the java class file, you will have to add one more condition to make the class exception free.
          Regards
          Kapil
          (0) 
  2. Andrew Crawley
    Hello

    I found that the Filter symbol was not appearing at the top of the column correctly.

    There were also some problems with Netweaver ’04s with deprecated methods of IWDTable etc.

    I have take the liberty of modifying the code.

    Anyway, here is the code.

    package nw.etsa.net.au.utils;

    import java.util.Comparator;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.StringTokenizer;

    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDAbstractDropDownByIndex;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDAbstractDropDownByKey;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDAbstractInputField;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDAbstractTableColumn;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDCaption;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDImage;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDLink;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDProgressIndicator;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDRadioButton;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTable;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTableCellEditor;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTableColumn;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTextEdit;
    import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTextView;
    import com.sap.tc.webdynpro.progmodel.api.IWDAction;
    import com.sap.tc.webdynpro.progmodel.api.IWDCustomEvent;
    import com.sap.tc.webdynpro.progmodel.api.IWDMessageManager;
    import com.sap.tc.webdynpro.progmodel.api.IWDNode;
    import com.sap.tc.webdynpro.progmodel.api.IWDNodeElement;
    import com.sap.tc.webdynpro.progmodel.api.IWDViewElement;
    import com.sap.tc.webdynpro.progmodel.api.WDCopyService;

    /**
    * Helper class that makes a Web Dynpro table UI element filterable (column-wise).
    */
    public final class TableFilter
    {
      /**
       * Creates a table filter for the given table using the given filter action.
       * This constructor must be called from wdDoModifyView(), but
       * usually only when that hook is called for the first time. Store the newly
       * created instance in a context attribute with Java native type
       * com.sap.tc.webdynpro.tests.utils.TableFilter.
       * The given filter action’s event handler will be bound to the onAction
       * event of filter columns and must at least call this table filter’s
       * filter(wdEvent) method.
       *
       * When a table is made filterable its onAction event (incl.
       * parameter mapping) is changed. Furthermore if a header is present for that
       * column then the header’s imageSource property is also changed
       * to indicate the currently filtered column.
       *
       * @see filter()
       * @see com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTable
       */
      public TableFilter(IWDTable table, IWDAction filterAction)
      {
        this.table = table;

        // sanity checks
        if (filterAction == null)
          throw new IllegalArgumentException(“Filter action must be given”);
        if (table == null)
          throw new IllegalArgumentException(“Table must be given”);
        if (table.bindingOfDataSource() == null)
          throw new IllegalArgumentException(“Data source of table with id ‘” + table.getId() + “‘ must be bound”);

        String dataSourcePrefix = table.bindingOfDataSource() + “.”;
        int index = 0;
       
         //for (Iterator it = table.iterateColumns(); it.hasNext(); ++index)
         for (Iterator it = table.iterateGroupedColumns(); it.hasNext(); ++index)
        {
             // for every column: try to make it bindable
          IWDTableColumn column = (IWDTableColumn) it.next();

           // attribute must be determined
          String bindingOfPrimaryProperty = bindingOfPrimaryProperty(column.getTableCellEditor());
         
          if ( bindingOfPrimaryProperty == null
              || !bindingOfPrimaryProperty.startsWith(dataSourcePrefix) )
              continue; // no attribute found or outside of data source
         
          String attributeName = bindingOfPrimaryProperty.substring(dataSourcePrefix.length());
          if (attributeName.indexOf(‘.’) >= 0)
          continue; // attribute not immediately below data source
           
          
           table.setOnFilter(filterAction);
         }
      }
     
      /**
       * This method must be called from the event handler of this table filter’s
       * filter action. It performs the actual filter operation.
       */
      public void filter(IWDCustomEvent wdEvent,IWDNode dataSource, IWDNode originalSource, IWDNode filterNode, IWDMessageManager manager)
      {
        // find the things we need
        String refinedFilter = null;
        String field = null;
        String attributeName = null;
        String bindingOfPrimaryProperty = null;
         String dataSourcePrefix = table.bindingOfDataSource() + “.”;
         IWDAbstractTableColumn[] columns = table.getGroupedColumns();
         String path = null;
         String attribute = null;
         StringTokenizer tokenizer = null;
         
         dataSource.invalidate();
         WDCopyService.copyElements(originalSource,dataSource);
        int totalElements = originalSource.size();
        for(int i = totalElements – 1; i >= 0 ; i–)
        {
             IWDNodeElement element = dataSource.getElementAt(i);
              for(int j =0; j<columns.length; j++ )
              {
                   IWDTableColumn colmn = (IWDTableColumn) columns[j];
                   
                   bindingOfPrimaryProperty = bindingOfPrimaryProperty(colmn.getTableCellEditor());
                   
                   if(bindingOfPrimaryProperty != null
                      && bindingOfPrimaryProperty.startsWith(dataSourcePrefix)){
                        attributeName = bindingOfPrimaryProperty.substring(dataSourcePrefix.length());
                      }
                   
                   if(attributeName.indexOf(‘.’)<0)
                        field = element.getAttributeAsText(attributeName);

                   if(colmn.bindingOfFilterValue() != null)
                   {
                        path = colmn.bindingOfFilterValue();
                        
                        tokenizer = new StringTokenizer(path, “.”);
                        
                        while(tokenizer.hasMoreTokens())
                        {
                             attribute = tokenizer.nextToken();     
                        }
                        
                        refinedFilter = refineFilterString(filterNode.getCurrentElement().getAttributeAsText(attribute));
                        
                        
                        if(!(field.matches(refinedFilter)))
                        {
                             dataSource.removeElement(element);
                        }

                   }
              }
             
        }

         for(int j =0; j<columns.length; j++ )
         {
                   IWDTableColumn colmn = (IWDTableColumn) columns[j];
                   
                   bindingOfPrimaryProperty = bindingOfPrimaryProperty(colmn.getTableCellEditor());
                   
                   if(bindingOfPrimaryProperty != null
                      && bindingOfPrimaryProperty.startsWith(dataSourcePrefix)){
                        attributeName = bindingOfPrimaryProperty.substring(dataSourcePrefix.length());
                      }
              
                   refinedFilter = refineFilterString(filterNode.getCurrentElement().getAttributeAsText(attributeName));
                     
              if (refinedFilter.equals(“.*”))
                   colmn.getHeader().setImageSource(null);
              else
                   if (colmn.getHeader() != null)
                             colmn.getHeader().setImageSource(“~sapicons/s_b_filt.GIF”);
                   else
                             colmn.getHeader().setImageSource(null);
         }   
    }

      /**
       * Returns the binding of the given table cell editor’s property that is
       * considered “primary” or null if no such binding exists or no
       * such property can be determined.
       */
      private static final String bindingOfPrimaryProperty(IWDTableCellEditor editor)
      {
        return editor instanceof IWDViewElement
          ? bindingOfPrimaryProperty((IWDViewElement) editor)
          : null;
      }

      /**
       * Returns the binding of the given view element’s property that is
       * considered “primary” or null if no such binding exists or no
       * such property can be determined.
       */
      private static final String bindingOfPrimaryProperty(IWDViewElement element)
      {
        if (element instanceof IWDAbstractDropDownByIndex)
          return ((IWDAbstractDropDownByIndex) element).bindingOfTexts();
        if (element instanceof IWDAbstractDropDownByKey)
          return ((IWDAbstractDropDownByKey) element).bindingOfSelectedKey();
        if (element instanceof IWDAbstractInputField)
          return ((IWDAbstractInputField) element).bindingOfValue();
        if (element instanceof IWDCaption)
          return ((IWDCaption) element).bindingOfText();
    //    if (element instanceof IWDCheckBox)
    //      return ((IWDCheckBox) element).bindingOfChecked();
        if (element instanceof IWDLink)
          return ((IWDLink) element).bindingOfText();
        if (element instanceof IWDProgressIndicator)
          return ((IWDProgressIndicator) element).bindingOfPercentValue();
        if (element instanceof IWDRadioButton)
          return ((IWDRadioButton) element).bindingOfSelectedKey();
        if (element instanceof IWDTextEdit)
          return ((IWDTextEdit) element).bindingOfValue();
        if (element instanceof IWDTextView)
          return ((IWDTextView) element).bindingOfText();
         //if (element instanceof IWDImage)
         //  return ((IWDImage)element).bindingOfAlt(); 

        return null;
      }

      /**
       * Instance of a comparator according to the ordering imposed by the
       * implementation of Comparable.
       */
      private static final Comparator DEFAULT = new Comparator()
      {
        /**
         * Compares the given objects according to the ordering imposed by the first
         * ones compareTo(Object) function. Furthermore, null
         * is treated to be less than any object.
         *
         * @see java.lang.Comparable#compareTo(java.lang.Object)
         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
         */
        public int compare(Object o1, Object o2)
        {
          if (o1 == null && o2 == null)
            return 0;
          if (o1 == null)
            return -1;
          if (o2 == null)
            return +1;
     
          return ((Comparable) o1).compareTo((Comparable) o2);
        }
      };
       
      /**
       * Map of table column to comparator (ReversableComparator)
       * used for sorting that column (sortable columns only).
       */
      private final Map comparatorForColumn = new HashMap();

      /**
       * Column that is currently sorted.
       */
      private IWDTableColumn currentlyFilteredColumn;

      /**
       * The table to be sorted.
       */
      private final IWDTable table;

      /**
       * Generic comparator that compares node elements by a given attribute with
       * the help of a given comparator.
       */
      public final class NodeElementByAttributeComparator implements Comparator
      {
        /**
         * Creates a new comparator for the given attribute name that compares values
         * of that attribute according to the natural ordering of that attribute’s
         * type (which must implement java.lang.Comparable).
         */
        public NodeElementByAttributeComparator(String attributeName)
        {
          this(attributeName, null, false);
        }
     
        /**
         * Creates a new comparator for the given attribute name that compares values
         * of that attribute with the help of the given comparator. If no comparator
         * is given, the natural ordering of that attribute’s type is used.
         */
        public NodeElementByAttributeComparator(String attributeName, Comparator comparator)
        {
          this(attributeName, comparator, false);
        }
     
        /**
         * Creates a new comparator for the given attribute name that compares values
         *of that attribute either as objects (i.e. “in internal format”) or as text
         * (i.e. “in external format”) as indicated. The ordering is the natural
         * ordering of that attribute’s type (which must implement
         * java.lang.Comparable) in case objects are compared or the
         * natural ordering of java.lang.String in case texts are compared.
         */
        public NodeElementByAttributeComparator(String attributeName, boolean compareAsText)
        {
          this(attributeName, null, compareAsText);
        }
     
        /**
         * Internal constructor.
         */
        private NodeElementByAttributeComparator(String attributeName,
          Comparator comparator, boolean compareAsText)
        {
          if (attributeName == null)
            throw new IllegalArgumentException(“Attribute name must not be null”);
          if (comparator == null)
            comparator = DEFAULT;
     
          this.attributeName = attributeName;
          this.comparator = comparator;
          this.compareAsText = compareAsText;
        }
     
        /**
         * Compares the given objects which must be instances of IWDNodeElement
         * according to the values of the attribute given at construction time
         * with the help of the comparator given at construction time.
         *
         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
         * @see com.sap.tc.webdynpro.progmodel.api.IWDNodeElement
         */
        public int compare(Object o1, Object o2)
        {
          IWDNodeElement element1 = (IWDNodeElement) o1;
          IWDNodeElement element2 = (IWDNodeElement) o2;
          Object attributeValue1 = compareAsText
            ? element1.getAttributeAsText(attributeName)
            : element1.getAttributeValue(attributeName);
          Object attributeValue2 = compareAsText
            ? element2.getAttributeAsText(attributeName)
            : element2.getAttributeValue(attributeName);
         
          return comparator.compare(attributeValue1, attributeValue2);
        }
     
        /**
         * Name of the attribute used for comparisons.
         */
        private final String attributeName;
       
        /**
         * Comparator used for comparing the attribute’s values.
         */
        private final Comparator comparator;
       
        /**
         * Indicates whether attribute values are compared as text (as opposed to
         * “as objects”).
         */
        private final boolean compareAsText;
      }

      /**
       * Comparator that knows how to reverse another comparator’s sort direction.
       */
      private final class ReversableComparator implements Comparator
      {
        /**
         * Creates a new reversable comparator for the given other comparator.
         */
        public ReversableComparator(Comparator comparator)
        {
          if (comparator == null)
            throw new IllegalArgumentException(“Comparator must not be null”);
     
          this.comparator = comparator;
        }
     
        /**
         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
         */
        public int compare(Object o1, Object o2)
        {
          return reversed
            ? -comparator.compare(o1, o2)
            :  comparator.compare(o1, o2);
        }
     
        /**
         * Tells whether the other comparator’s sort direction is reversed.
         */
        public boolean isReversed()
        {
          return reversed;
        }
     
        /**
         * Determines whether the other comparator’s sort direction is reversed.
         */
        public void setReversed(boolean reversed)
        {
          this.reversed = reversed;
        }
       
        /**
         * Toggles the current “sort direction”.
         */
        public void toggleReversed()
        {
          reversed = !reversed;
        }
     
        /**
         * The other comparator.
         */
        private final Comparator comparator;
     
        /**
         * Whether the other comparator’s sort direction is reversed.
         */
        private boolean reversed;
      }
     
      public String refineFilterString (String filterString)
      {
         if ( ( null == filterString ) || ( 0 >= filterString.length() ) )
         {
              filterString = “.*”;  //show All
         }

         if ( ! filterString.equalsIgnoreCase(“.*”) )
         {
              if(filterString.indexOf(‘*’)>=0)
              {
                   filterString = filterString.replaceAll(“\\*”,”\\.\\*”);
              }
              else
              {
                   filterString = “.*” + filterString + “.*”;
              }
              if(!filterString.endsWith(“*”) && !filterString.startsWith(“.*”) && (filterString.indexOf(‘*’)> 0))
              {
                   filterString = “.*” + filterString + “.*”;
              }
         }
         return filterString;
      }
     
    }

    Regards
    Andrew

    (0) 
  3. Prathamesh Dalvi
    Hi Kapil,

    This is an excellent example of filtering values in a table. I have incorporated the same in my webdynpro project. But the only problem is, that the filter is case sensitive. The table values are database table values (uppercase). Thus when we search for a value in lowercase, the search returns nothing. Thus I would like to make the search case in-sensitive.
    Can you please help me with this.

    Regards,
    Prathamesh

    (0) 
    1. Csaba Molnar
      Hi Prathamesh,

      I have just found out how to make the search Case-insensitive.
      At the end of the refineFilterString method you have the statement
      return filterString;
      If you change this to the following:
      return “(?i)” + filterString;
      the filter will be case-insensitive.
      This works because filterString contains a regular expression and including (?i) in a regexp makes it case-insensitive.
      For further info see http://www.exampledepot.com/egs/java.util.regex/Case.html

      Regards,
      Csaba Molnar

      (0) 
  4. Kelly Ferguson
    There is a method called bindingOfPrimaryProperty()…..

    Somebody commented out the lines for the checkbox…

    If your table has some checkboxs…then you need to uncomment these.

    (0) 
  5. Umang Mathur
    Hi Kapil, could you please provide me with the tablefilter java class file. You cam mail it to me       
    at umang.mathur@ril.com
    This table filtering is not working in my case, could it be because I have already used sorting for that table?
    The code which I have used is:
    public static void wdDoModifyView(IPrivateTravelCompanyPaidView wdThis, IPrivateTravelCompanyPaidView.IContextNode wdContext, com.sap.tc.webdynpro.progmodel.api.IWDView view, boolean firstTime)
      {
        //@@begin wdDoModifyView
         if ( firstTime )
         {
         if ( null != view )
         {
         IWDTable table = (IWDTable) view.getElement(“Table_0_1_2_3_4”);
         wdContext.currentContextElement().setTableFilter(new TableFilter(table,wdThis.wdGetFilterAction()));
         }
         }
    }

    public void onActionFilter(com.sap.tc.webdynpro.progmodel.api.IWDCustomEvent wdEvent )
      {
        //@@begin onActionFilter(ServerEvent)
       
         wdContext.currentContextElement().getTableFilter().filter(wdEvent,wdContext.nodeVnTableDataSource(),wdContext.nodeRequestapproved(),wdContext.nodeVnFilters(),null);
         
      }
    Could you please help me out.

    (0) 

Leave a Reply