Skip to Content
avatar image
Former Member

Table Filter

unable to use table filter,always showing exception

Add comment
10|10000 characters needed characters exceeded

  • Follow
  • Get RSS Feed

4 Answers

  • Best Answer
    avatar image
    Former Member
    Dec 18, 2008 at 07:44 AM

    Hi,

    Use this code for the tablefilter.java class

    package com.hcl.tablefiltercomp.utils;

    import java.math.BigDecimal;

    import java.sql.Date;

    import java.text.Collator;

    import java.text.SimpleDateFormat;

    import java.util.ArrayList;

    import java.util.Collection;

    import java.util.Comparator;

    import java.util.Enumeration;

    import java.util.HashMap;

    import java.util.Iterator;

    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.IWDCheckBox;

    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.IWDTableColumnGroup;

    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.IWDAttributeInfo;

    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;

    import com.sap.tc.webdynpro.services.sal.localization.api.WDResourceHandler;

    import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;

    import com.sun.org.apache.xerces.internal.impl.xpath.regex.ParseException;

    public final class TableFilter {

    public TableFilter(IWDTable table, IWDAction filterAction, IWDNode sourceNode, Hashtable hashicons) {

    init(table, filterAction, sourceNode, hashicons);

    }

    private void init(IWDTable table, IWDAction filterAction, IWDNode sourceNode, Hashtable hashicons) {

    this.table = table;

    if (sourceNode == null)

    throw new IllegalArgumentException("SourceNode must be given");

    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");

    this.sourceNode = sourceNode;

    if (hashicons != null) {

    this.hashIcon = new Hashtable();

    this.hashIcon = hashicons;

    }

    String dataSourcePrefix = table.bindingOfDataSource() + ".";

    //works on columns

    setFiltersForColumns(dataSourcePrefix, table.iterateColumns(), sourceNode);

    setFiltersForColumns(dataSourcePrefix, table.iterateGroupedColumns(), sourceNode);

    table.setOnFilter(filterAction);

    filterAction.setEnabled(true);

    this.sourceNode = sourceNode;

    }

    private void setFiltersForColumns(String dataSourcePrefix, Iterator columnIterator, IWDNode sourceNode) {

    attributeSource = new Hashtable();

    for (Iterator iter = sourceNode.getNodeInfo().iterateAttributes(); iter.hasNext();) {

    IWDAttributeInfo attribInfo = (IWDAttributeInfo) iter.next();

    String attribName = attribInfo.getName();

    attributeSource.put(attribName, attribInfo.getSimpleType().getBuiltInType());

    }

    int index = 0;

    for (Iterator it = columnIterator; it.hasNext(); ++index) { // for every column: try to make it bindable

    IWDAbstractTableColumn abstractColumn = (IWDAbstractTableColumn) it.next();

    if (abstractColumn instanceof IWDTableColumn) {

    IWDTableColumn column = (IWDTableColumn) abstractColumn;

    String columnId = column.getId();

    Comparator comparator = null;

    NodeElementByAttributeComparator elementComparator = null;

    String bindingOfPrimaryProperty = bindingOfPrimaryProperty(column.getTableCellEditor());

    String attributeName = null;

    if (bindingOfPrimaryProperty == null || !bindingOfPrimaryProperty.startsWith(dataSourcePrefix)) {

    if (hashIcon == null) {

    continue;

    } else {

    if (!hashIcon.containsKey(columnId)) {

    continue;

    } else {

    attributeName = hashIcon.get(columnId).toString();

    }

    }

    } else {

    attributeName = bindingOfPrimaryProperty.substring(dataSourcePrefix.length());

    if(hashIcon != null){

    if (hashIcon.containsKey(columnId))

    attributeName = hashIcon.get(columnId).toString();

    }else{

    hashIcon = new Hashtable();

    }

    }

    String attributeInfo = null;

    if (attributeSource.containsKey(attributeName))

    attributeInfo = (String) attributeSource.get(attributeName);

    elementComparator = new NodeElementByAttributeComparator(attributeInfo, column, comparator);

    filterForColumn.put(attributeName, elementComparator);

    } else if (abstractColumn instanceof IWDTableColumnGroup) {

    IWDTableColumnGroup columnGroup = (IWDTableColumnGroup) abstractColumn;

    setFiltersForColumns(dataSourcePrefix, columnGroup.iterateColumns(), sourceNode);

    } }

    }

    public void filter( IWDNode allDataNode, IWDNode targetNode) {

    if (allDataNode != null) {

    this.sourceNode = allDataNode;

    }

    HashMap FilterValuesByAttribute = prepareFilterValuesByAttribute();

    ArrayList ar = new ArrayList();

    for (int i = 0; i < sourceNode.size(); i++) {

    IWDNodeElement el = sourceNode.getElementAt(i);

    boolean addelement = true;

    for (Enumeration e = this.attributeSource.keys(); e.hasMoreElements();) {

    String attributeName = e.nextElement().toString();

    Object o1 = el.getAttributeValue(attributeName);

    if (FilterValuesByAttribute.containsKey(attributeName) && filterForColumn.containsKey(attributeName)) {

    NodeElementByAttributeComparator elementComparator = (NodeElementByAttributeComparator) filterForColumn.get(attributeName);

    HashMap valuehash = new HashMap();

    valuehash = (HashMap) FilterValuesByAttribute.get(attributeName);

    String attributetype = elementComparator.attributeInfo;

    Comparator comparator = elementComparator.comparator;

    if (valuehash.size()==0){

    elementComparator.column.setIsFiltered(false);

    } else {

    elementComparator.column.setIsFiltered(true);

    }

    addelement = this.evaluateFilteredAttributeValue(valuehash, attributetype, comparator, o1);

    if (!addelement)

    break;

    }

    if (!addelement)

    break;

    }

    if (!addelement)

    continue;

    IWDNodeElement targetElement = targetNode.createElement();

    WDCopyService.copyCorresponding(el, targetElement);

    ar.add(targetElement);

    }

    targetNode.bind(ar);

    }

    public void deleteAllDataNodeElement(

    IWDNode sourceNode,

    IWDNode targetNode,

    String attributeRowId,

    ArrayList idsToDelete) {

    for (int j = sourceNode.size() - 1; j >= 0; --j) {

    IWDNodeElement els = sourceNode.getElementAt(j);

    String elsid = els.getAttributeAsText(attributeRowId);

    if (!idsToDelete.contains(elsid))

    continue;

    sourceNode.removeElement(els);

    }

    for (int i = targetNode.size() - 1; i >= 0; --i) {

    IWDNodeElement elt = targetNode.getElementAt(i);

    String eltid = elt.getAttributeAsText(attributeRowId);

    if (!idsToDelete.contains(eltid))

    continue;

    targetNode.removeElement(elt);

    }

    }

    /*Update all data node if the table node has been updated

    • if a row has been added in the table node,

    • the all data node can also be updated by using the using the flag addRowOnFly

    */

    public void updateAllDataNodeElement(

    IWDNode sourceNode,

    IWDNode targetNode,

    String attributeRowId,

    boolean addRowOnFly) {

    HashMap hashs = new HashMap();

    HashMap hasht = new HashMap();

    HashMap hashup = new HashMap();

    IWDNodeElement elt;

    IWDNodeElement els;

    for (int j = sourceNode.size() - 1; j >= 0; --j) {

    els = sourceNode.getElementAt(j);

    String elsid = els.getAttributeAsText(attributeRowId);

    hashs.put(elsid, els);

    }

    for (int i = 0; i < targetNode.size(); i++) {

    elt = targetNode.getElementAt(i);

    String rowid = elt.getAttributeAsText(attributeRowId);

    boolean isInSource = hashs.containsKey(rowid);

    if (isInSource && !elt.isChangedByClient())

    continue;

    if (!isInSource) {

    els = sourceNode.createElement();

    WDCopyService.copyCorresponding(elt, els);

    sourceNode.addElement(els);

    } else {

    els = (IWDNodeElement) hashs.get(rowid);

    WDCopyService.copyCorresponding(elt, els);

    }

    }

    }

    /*find if an column has a valid filter

    • and if so parse the filter and put in a hash table

    */

    private HashMap prepareFilterValuesByAttribute() {

    HashMap hashmap = new HashMap();

    for (Enumeration e = this.attributeSource.keys(); e.hasMoreElements();) {

    String attributeName = e.nextElement().toString();

    if (filterForColumn.containsKey(attributeName)) {

    NodeElementByAttributeComparator elementComparator = (NodeElementByAttributeComparator) filterForColumn.get(attributeName);

    IWDTableColumn column = elementComparator.column;

    column.setIsFiltered(false);

    String filtervalue = column.getFilterValue();

    if (filtervalue == null || filtervalue.trim().length() == 0) {

    continue;

    }

    String attributetype = elementComparator.attributeInfo;

    HashMap valuehash = new HashMap();

    valuehash = this.detectFilterSign(filtervalue);

    hashmap.put(attributeName, valuehash);

    }

    }

    return hashmap;

    }

    /* generate a filter value into the right object */

    private Object generateObject(String filtervalue, String attributetype, String sign) {

    Object o2 = filtervalue;

    try {

    if (filtervalue == null) {

    return o2;

    }

    if (attributetype.equalsIgnoreCase("decimal")) {

    o2 = new BigDecimal(filtervalue);

    }

    if (attributetype.equalsIgnoreCase("double")) {

    o2 = new Double((filtervalue));

    }

    if (attributetype.equalsIgnoreCase("long")) {

    o2 = new Long(filtervalue);

    }

    if (attributetype.equalsIgnoreCase("float")) {

    o2 = new Float(filtervalue);

    }

    if (attributetype.equalsIgnoreCase("short")) {

    o2 = new Short(filtervalue);

    }

    if (attributetype.equalsIgnoreCase("integer")) {

    o2 = new Integer(filtervalue);

    }

    if (attributetype.equalsIgnoreCase("long")) {

    o2 = new Long(filtervalue);

    }

    if (attributetype.equalsIgnoreCase("date")) {

    try {

    java.util.Date dtTmp = new java.util.Date(filtervalue);

    SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd");

    o2 = Date.valueOf(template.format(dtTmp));

    } catch (Exception ex) {

    o2 = filtervalue;

    }

    }

    if (attributetype.equalsIgnoreCase("time")) {

    try {

    SimpleDateFormat timeformater = new SimpleDateFormat("hh:mm:ss", WDResourceHandler.getCurrentSessionLocale());

    o2 = timeformater.parse(filtervalue);

    } catch (ParseException ex) {

    o2 = filtervalue;

    }

    }

    if (attributetype.equalsIgnoreCase("boolean")) {

    if (sign.equalsIgnoreCase(NE)) {

    o2 = new Boolean(true);

    } else {

    o2 = new Boolean(true);

    }

    }

    return o2;

    }catch (Exception ex){

    return filtervalue;

    }

    }

    /*compare object according the filter */

    private boolean evaluateFilteredAttributeValue(HashMap filter, String attributetype, Comparator comparator, Object o1) {

    boolean accept = true;

    try{

    if (filter == null || filter.size() == 0)

    return accept;

    if (attributetype == null || attributetype.trim().length() == 0)

    return accept;

    Object o2 = filter.get("value1");

    if (filter.get("sign") == null) {

    if (o2 != null) {

    String value1 = filter.get("value1").toString();

    o2 = generateObject(value1, attributetype, null);

    }

    int compared = comparator.compare(o1, o2);

    if (!attributetype.equalsIgnoreCase("string")) {

    if (compared == 0) {

    return accept;

    } else {

    return (!accept);

    }

    } else {

    if (compared >= 0) {

    return accept;

    } else {

    return (!accept);

    }

    }

    }

    String sign = filter.get("sign").toString();

    String place = filter.get("place").toString();

    if (sign.equalsIgnoreCase(EQ) && place.equalsIgnoreCase("S")) {

    if (o2 != null) {

    String value1 = filter.get("value1").toString();

    o2 = generateObject(value1, attributetype, sign);

    } else {

    if (attributetype.equalsIgnoreCase("boolean")) {

    o2 = generateObject("", attributetype, sign);

    }

    if (attributetype.equalsIgnoreCase("string") && o1 != null && o1.toString().trim().length() == 0) {

    o2 = generateObject("", attributetype, sign);

    }

    }

    int compared = comparator.compare(o1, o2);

    if (!attributetype.equalsIgnoreCase("string")) {

    if (compared == 0) {

    return accept;

    } else {

    return (!accept);

    }

    } else {

    if (compared >= 0) {

    return accept;

    } else {

    return (!accept);

    }

    }

    }

    if (sign.equalsIgnoreCase(NE) && place.equalsIgnoreCase("S")) {

    if (o2 != null) {

    String value1 = filter.get("value1").toString();

    o2 = generateObject(value1, attributetype, sign);

    } else {

    if (attributetype.equalsIgnoreCase("boolean")) {

    o2 = generateObject("", attributetype, sign);

    }

    if (attributetype.equalsIgnoreCase("string") && o1 != null && o1.toString().trim().length() == 0) {

    o2 = generateObject("", attributetype, sign);

    }

    }

    int compared = comparator.compare(o1, o2);

    if (!attributetype.equalsIgnoreCase("string")) {

    if (compared == 0) {

    return !accept;

    } else {

    return (accept);

    }

    } else {

    if (compared >= 0) {

    return !accept;

    } else {

    return (accept);

    }

    }

    }

    if (sign.equalsIgnoreCase(R) && place.equalsIgnoreCase("S")) {

    if (o2 != null) {

    String value1 = filter.get("value1").toString();

    o2 = generateObject(value1, attributetype, sign);

    }

    int compared = comparator.compare(o1, o2);

    if (compared > 0) {

    return !accept;

    } else {

    return (accept);

    }

    }

    if (sign.equalsIgnoreCase(R) && place.equalsIgnoreCase("E")) {

    if (o2 != null) {

    String value1 = filter.get("value1").toString();

    o2 = generateObject(value1, attributetype, sign);

    }

    int compared = comparator.compare(o1, o2);

    if (compared >= 0) {

    return accept;

    } else {

    return (!accept);

    }

    }

    Object o3 = filter.get("value2").toString();

    if (sign.equalsIgnoreCase(R) && place.equalsIgnoreCase("M")) {

    boolean accept1 = true;

    boolean accept2 = true;

    if (o2 != null) {

    String value1 = filter.get("value1").toString();

    o2 = generateObject(value1, attributetype, sign);

    }

    if (o3 != null) {

    String value1 = filter.get("value2").toString();

    o3 = generateObject(value1, attributetype, sign);

    }

    int compared = comparator.compare(o1, o2);

    if (compared >= 0) {

    accept1 = accept;

    } else {

    accept1 = !accept;

    }

    int compared2 = comparator.compare(o1, o3);

    if (compared2 > 0) {

    accept2 = !accept;

    } else {

    accept2 = accept;

    }

    if (accept1 && accept2) {

    return accept;

    } else {

    return !accept;

    }

    }

    return accept;

    } catch (Exception ex ){

    return accept;

    }

    }

    /* parse the filter */

    private HashMap detectFilterSign(String value) {

    HashMap ar = new HashMap();

    String val1 = null;

    String val2 = null;

    String sign = null;

    String place = null;

    if (null == value) {

    ar.put("value1", value);

    ar.put("sign", null);

    return ar;

    }

    if (!value.startsWith(EQ) & !value.startsWith(NE) & value.indexOf(R) < 0) {

    ar.put("value1", value.trim());

    ar.put("sign", null);

    return ar;

    }

    if (value.startsWith(EQ)) {

    if (value.trim().length() > 1) {

    val1 = value.substring(1);

    } else {

    val1 = "";

    val1 = null;

    }

    sign = EQ;

    place = "S";

    ar.put("value1", val1);

    ar.put("sign", EQ);

    ar.put("place", place);

    return ar;

    }

    if (value.startsWith(NE)) {

    if (value.trim().length() > 1) {

    val1 = value.substring(1);

    } else {

    val1 = "";

    val1 = null;

    }

    sign = NE;

    place = "S";

    ar.put("value1", val1);

    ar.put("sign", NE);

    ar.put("place", place);

    return ar;

    }

    if (value.startsWith(R)) {

    if (value.trim().length() > 1) {

    val1 = value.substring(1);

    } else {

    val1 = "";

    val1 = null;

    }

    sign = R;

    place = "S";

    ar.put("value1", val1);

    ar.put("sign", R);

    ar.put("place", place);

    return ar;

    }

    if (value.endsWith(R)) {

    if (value.trim().length() > 1) {

    val1 = value.substring(0, value.length() - 1);

    } else {

    val1 = "";

    val1 = null;

    }

    sign = R;

    place = "E";

    ar.put("value1", val1);

    ar.put("sign", R);

    ar.put("place", place);

    return ar;

    }

    if (value.indexOf(R) > 0) {

    sign = R;

    place = "M";

    val1 = value.substring(0, value.indexOf(R));

    val2 = value.substring(value.indexOf(R) + 1);

    ar.put("value1", val1);

    ar.put("value2", val2);

    ar.put("sign", R);

    ar.put("place", place);

    return ar;

    }

    if (ar.size() == 0)

    ar.put("value1", value);

    return ar;

    }

    private static final String bindingOfPrimaryProperty(IWDTableCellEditor editor) {

    return editor instanceof IWDViewElement ? bindingOfPrimaryProperty((IWDViewElement) editor) : null;

    }

    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();

    return null;

    }

    private static final Comparator DEFAULT = new Comparator() {

    public int compare(Object o1, Object o2) {

    if (o1 == null && o2 == null) {

    return 0;

    }

    if (o1 == null) {

    return -1;

    }

    if (o2 == null) {

    return -1;

    }

    if (o1 instanceof Boolean && o2 instanceof Boolean) {

    return o1.toString().compareTo(o2.toString());

    }

    if (o1 instanceof Date && o2 instanceof Date) {

    return ((java.util.Date) o1).compareTo((java.util.Date) o2);

    }

    if (o1 instanceof String && o2 instanceof String) {

    Collator collate = Collator.getInstance(WDResourceHandler.getCurrentSessionLocale());

    return o1.toString().toUpperCase(WDResourceHandler.getCurrentSessionLocale()).indexOf(

    o2.toString().toUpperCase(WDResourceHandler.getCurrentSessionLocale()));

    }

    return ((Comparable) o1).compareTo((Comparable) o2);

    }

    };

    private Hashtable filterForColumn = new Hashtable();

    private IWDTable table = null;

    private Hashtable attributeSource = null;

    private Hashtable sourceNodeHash = null;

    private Hashtable hashIcon = null;

    private IWDNode sourceNode = null;

    private IWDNode targetNode = null;

    String EQ = "=";

    String NE = "#";

    String R = "~";

    public final class NodeElementByAttributeComparator {

    public NodeElementByAttributeComparator(String attributeInfo, IWDTableColumn column, Comparator comparator) {

    this(attributeInfo, column, comparator, false, new ArrayList());

    }

    private NodeElementByAttributeComparator(

    String attributeInfo,IWDTableColumn column,

    Comparator comparator,

    boolean compareAsText,

    Collection subNodes) {

    if (attributeInfo == null)

    throw new IllegalArgumentException("attributeInfo must not be null");

    if (column == null)

    throw new IllegalArgumentException("Column must not be null");

    if (comparator == null)

    comparator = DEFAULT;

    this.attributeInfo = attributeInfo;

    this.column = column;

    this.comparator = comparator;

    this.compareAsText = compareAsText;

    this.subNodes = subNodes;

    }

    private final Comparator comparator;

    private final boolean compareAsText;

    private IWDTableColumn column;

    private Collection subNodes;

    private String attributeInfo;

    }

    }

    Regards

    Gayathri

    Add comment
    10|10000 characters needed characters exceeded

  • avatar image
    Former Member
    Dec 18, 2008 at 06:12 AM

    Hi,

    Please follow this blog.

    Generic Web Dynpro Java Table Filter

    If u are getting any null pointer exception then follow the below message

    Table Filter for NW04 / NullpointerException

    2007-01-27 06:14:24 Peter Vignet Business Card [Reply]

    Matthias:

    To fix the bug of

    if (hashIcon.comtainsKey(columnId). I see the one of the following ways:

    a) check if the hashIcon is not null before

    (small code change required)

    or b) give to the constuctor an empty hashtable ( no code change required)

    Thanks, Best regards

    Regards

    Gayathri

    Add comment
    10|10000 characters needed characters exceeded

    • Former Member

      hi gayathri, actually i am getting null pointer exception.

      The thing u explain can u plz give me a code snippet of that which u used,,,I tried my self but was not able to get through it.

      Thanx in Advance

  • avatar image
    Former Member
    Dec 18, 2008 at 06:30 AM

    Hi,

    Check the following code in the table filter class.

    if (bindingOfPrimaryProperty == null || !bindingOfPrimaryProperty.startsWith(dataSourcePrefix)) {

    if (hashIcon == null) {

    * hashIcon = new HasTable();* continue;

    } else {

    if (!hashIcon.containsKey(columnId)) {

    continue;

    } else {

    attributeName = hashIcon.get(columnId).toString();

    }

    }

    } else {

    attributeName = bindingOfPrimaryProperty.substring(dataSourcePrefix.length());

    if (hashIcon.containsKey(columnId))

    attributeName = hashIcon.get(columnId).toString();

    }

    In that if (hashIcon == null){

    hashIcon = new HashTable();

    add this line.

    Regards

    Gayathri

    Add comment
    10|10000 characters needed characters exceeded

  • avatar image
    Former Member
    Dec 18, 2008 at 06:41 AM

    Hi,

    Go through this thread

    Table Filter

    Regards

    Padma N

    Add comment
    10|10000 characters needed characters exceeded