Wednesday, November 18, 2015

Querying Using Non-Persistent Fields

I recently ran into a situation where I wanted to make a non-persistent field searchable in a table filter. The requirement was to make a table searchable based on whether a complex condition was true or false.

First, I created a non-persistent field on the table through Database Configuration.

Next, I added the field to my table and made it filterable. This was done by adding filterable="true" to the tablecol definition.

<tablecol dataattribute="CID_DEFECT" id="cid_defect" filterable="true" inputmode="readonly"/>


Normally, non-persistent fields are not filterable. I created the following Java class to make non-persistent fields work in a table filter.  The package space is important because it requires replacing the value of a package protected field.

package psdi.mbo;

import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import psdi.security.UserInfo;
import psdi.util.MXException;

/**
 * An MboQuery object that will accept some non-persistent fields without
 * adding them to the where clause.  This is to permit a non-persistent field
 * in a table filter row where the sql logic is implemented in the databean.
 *
 * @author Martin Nichol
 *
 */
public class NPMboQbe extends MboQbe {

    /** the list of permitted non-persistent attributes and their values.  */
    private HashMap<String, String> permittedAttributes = new HashMap<String, String>();

    /**
     * Constructor.
     *
     * @param ms mbosetinfo of the owning mboset.
     * @param processML the processML flag of the owning mboset.
     * @param userInfo the user who owns the owning mboset
     * @param npAttributes the non-persistent attributes to permit.
     */
    public NPMboQbe(MboSetInfo ms, boolean processML, UserInfo userInfo, String[] npAttributes) {
        super(ms, processML, userInfo);
        initializeNpFields(npAttributes);
    }

    /**
     * @param ms mbosetinfo of the owning mboset.
     * @param l the user's locale.
     * @param tz the user's timezone.
     * @param ml the processML flag of the owning mboset.
     * @param userInfo the user who owns the owning mboset.
     * @param npAttributes the non-persistent attributes to permit.
     */
    public NPMboQbe(MboSetInfo ms, Locale l, TimeZone tz, boolean ml, UserInfo userInfo, String[] npAttributes) {
        super(ms, l, tz, ml, userInfo);
        initializeNpFields(npAttributes);
    }


    @Override
    public void setQbe(String attr, String expr) throws MXException {
        if (permittedAttributes.containsKey(attr)) {
            permittedAttributes.put(attr, expr);
        } else {
            super.setQbe(attr, expr);
        }
    }

    @Override
    public String getQbe(String attrStr) throws MXException {
        if (permittedAttributes.containsKey(attrStr)) {
            return permittedAttributes.get(attrStr);
        } else {
            return super.getQbe(attrStr);
        }
    }

    @Override
    public void resetQbe() {
        super.resetQbe();
        for (Map.Entry me : permittedAttributes.entrySet()) {
            me.setValue("");
        }
    }

    /**
     * From a list of attributes, initialize the map of values.
     * @param npAttributes the list of attributes to allow.
     */
    private void initializeNpFields(String[] npAttributes) {
        for (String attr : npAttributes) {
            permittedAttributes.put(attr, "");
        }
    }

    /**
     * Associate an NPMboQbe with a given MboSet.
     * @param msr the mboset to replace the MboQbe object.
     * @param npAttributes the list of attributes to allow.
     * @throws RemoteException if an RMI problem occurs.
     * @throws MXException if a Maximo problem occurs.
     */
    public static void assignNewQbe(MboSetRemote msr, String[] npAttributes) throws RemoteException, MXException {
        MboSet ms = (MboSet)msr;
        MboQbe qbe = new NPMboQbe(ms.getMboSetInfo(), ms.getClientLocale(), ms.getClientTimeZone(), ms.processML(), ms.getUserInfo(), npAttributes);
        ms.qbe = qbe;
    }
}


Then I extended the DataBean associated with the table to apply the sql driven by the non-persistent field. If a DataBean isn't defined for the table, create it and assign it.

@Override
public synchronized void setQbe(String attribute, String expression) throws MXException {
    super.setQbe(attribute, expression);
    if ("CID_DEFECT".equals(attribute)) {
       if (expression.contains("Y")) {
          getMboSet().setWhere("sql expression for condition true");
       } else if (expression.contains("N")) {
          getMboSet().setWhere("sql expression for condition false");
       } else {
          getMboSet().setWhere("");
       }
    }
}

@Override
protected void initialize() throws MXException, RemoteException {
    super.initialize();
    NPMboQbe.assignNewQbe(getMboSet(), new String[] { "CID_DEFECT" });
}

The non-persistent field can now affect the results displayed in the table.

2 comments:

  1. Much of this content is way beyond me, so in true paint shed form, I'm going to mention that you have some font skew, and code has window wrapping. I think you could make use of the assumption that in 2016, at your technical level, readers will have access to and be using larger monitors. Maybe a wider format is in order?

    Gosh, you are smarter than I remember.

    ReplyDelete
  2. Type mismatch: cannot convert from element type Object to Map.Entry on permittedAttributes.

    ReplyDelete