FORUM PLAN UPDATE
Date revision: This forum will remain open to new posts and responses until December 1, 2018. (After that date, you will still be able to view and search the forum.) Also, we're taking a second look at the best place to host future conversation. For now, keep using this forum, and stay tuned for more news.



Sep 9, 2015, 3:53 PM
453 Posts

Defining an ObjectDataSource

  • Category: Other
  • Platform: All
  • Release: 9.0.1
  • Role:
  • Tags: ObjectdataSource
  • Replies: 12

I have defined a Java Class called PaymentMap. Which I have defined as a viewScope managed bean called PayObj. The PaymentMap is a HashMap of <String, PaymentItem> where PaymentItem is an Object that contains all of the information about a payment (the PaymentMap may contain any number of PaymentItems). 

In the beforePageLoad I call a method PayObj.buildMap which get a collection of related payment documents and builds the PaymentMap. From this point on  everything is memory resident until I call the PayObj.saveItems which writes any changes out to the appropriate Notes Payment document/s. I have most of that working fairly well.

I have been able to programatically create a new PaymentItem and put it into the PaymentMap without problems.

My main Document which I will call a Payment Approval Document I have a repeat control that is bound to the viewScope.PayObj I use the Reapeat Index value to know which PaymentItem in the HashMap I'm working with so PayObj.get((rIndex + 1).tostring()).getAmount() gets the value of the Amount from the PaymentItem.

This is a long way around getting to the real issue:

In my table of repeats I have a button that sets viewScope.vsRIndex = rIndex and viewScope.vsShowPayment = true which renders a panel that contains a custom control for entry/edit of a PaymentItem.

I just can't figure out how to define the dataSource for the PaymentItem. Obviously the Save etc will not save to disk but back to the PaymentMap but I already have methods to do most of that. I have tried using an ObjectDataSource but I'm a bit confused (not a rare occurrence).

I was going to post this on StackOverFlow but it is really a continuation of my previous question here and also the question by 

Sep 9, 2015, 4:15 PM
589 Posts
Code Please

Please post the relevant snippest of your code...  the WHOLE Java Class please .... and the relavent Xpage bits also ... etc..

I'm not quite understanding your use of objectData and then also a viewScope variable...  

 

 

Sep 9, 2015, 6:15 PM
453 Posts
Not sure where to start

My repeat control that displays the MapObj is:

<xp:repeat id="repeatData"
      rows="10" value="#{viewScope.payObj}" var="payItem"
       indexVar="rIndex">
.
.
       <xp:button>

       <xp:eventHandler
           event="onclick" submit="true" refreshMode="partial"  refreshId="panelPaymentsContainer">
               <xp:this.action><![CDATA[#{javascript:try{viewScope.vsRowIndex = rIndex;
                viewScope.vsShowPayment = true
                break;
               }catch(e){
                  WFSUtils.sysOut("Error in calling dialogPayment " + e.tostring)
                  }}]]>
                                                                </xp:this.action>
                                                            </xp:eventHandler>

      </xp:button>

</xp:repeat>

 

the viewScope.vsRowIndex gets me a pointer to the PaymentItem. The HashMap is of <sting, PaymentItem> where the key value is an integer converted to a String. The keys are 1, 2, 3, .... N the indexVar is numbered 0, 1 2 3 .... N so when I retieve a PaymentItem from the HashMap the key I'm working with is   (rIndex + 1).toString() or (viewScope.vsRowIndex + 1).toString().

So to get the PaymentItem related to the click on the button it would be pItem:PaymentItem = PayObj.get((rIndex+1),toString()) if working withing the scope of the Repeat or pItem:PaymentItem = PayObj.get((viewScope.vsRowIndex +1 ),toString()). 

The panel that contains the control that I use for CRUD on the PaymentItem is rendered if vsShowPayment is true. I want to define a datasource on that panel that the fields in the ccPaymentEntry would access.

so I have no trouble getting the PaymentItem Object but how do I define the DataSource for the ccPaymentEntry. It can not be a document or view so I'm guessing it would be an ObjectDataSource but how do I define that. What would the field binding look like. I seem to remember a NotesIn9 video on something like this but I looked through the index and previewed a few but could not find the one I was looking for.

I have most all of the Java side of this working with dummied up data, So it displays correcctly in the Repeat  and the buildMap and saveItems works correctly so I'm stuck on defining the DataSource and binding it to the fields on the. 

My panel on the ccPaymentEntry looks like this:

<xp:panel id="panelPaymentData">
        <xp:this.data>
            <xe:objectData var="pItem" scope="request">
                <xe:this.createObject><![CDATA[#{javascript:var key:String = (viewScope.vsRowIndex + 1).toString();
return pItem = payObj.get(key);
}]]></xe:this.createObject>
            </xe:objectData>
        </xp:this.data>
<xp:text escape="true" id="expPayDate" value="#{pItem.expPayDate}">
       <xp:this.converter>
               <xp:convertDateTime type="date"></xp:convertDateTime>
         </xp:this.converter>
</xp:text>

</xp:panel>

 

The getter and setter for the field expPayDate are:

private Date expPayDate;

public Date getExpPayDate() {
        return expPayDate;
    }

    public void setExpPayDate(Date expPayDate) {
        this.expPayDate = expPayDate;
    }

Hope that explains what I'm looking for. Never done this before but really see lots of use for this sort of structure and it is probably what Michael G is looking for. fair bit of work getting the classes and methods worked out but I have several places where I can see using this. I picked this little payment processing because it only involves 4 or 5 fields. I built it previously using Notes documents to populate the repeat and read and wrote to the backend directly with each action. Using this process I can already see some significant improvements in speed just with some test data. Getting the TotalPayments is now entirely memory resident and requires no backend data access.

Sep 9, 2015, 8:57 PM
300 Posts
to start with
If your bean is a managed bean you don't use viewScope. If your bean is payObj than just say for the value in the repeat control payObj

Now, when you repeat you need to repeat over something that returns a Collection (maybe Arrays would work, I use Collections).

So, Collection myCol = new ArrayList();
myCol.add("test1")
myCol.add("test2")

then have a method in your bean that returns the collection.

Each member of the collection is referred to by using the var obj in the repeat. If you have a Collection of let's say persons and the person class has a getName() than you can say:

var.getName() in SSJS

or in Expression Lang... var.name

Doing this from memory... hope I got it right...

Howard
Sep 10, 2015, 12:33 PM
453 Posts
Think I'm getting closer ...

as per Howards suggestion I have created a method in the PaymentMap called allItems which returns an ArrayList of PaymentItem. Here is my method:

    public ArrayList<PaymentItem> allItems(){
        ArrayList<PaymentItem> rtn = new ArrayList<PaymentItem>();
        try{
            for (Integer n = 1; n <= internalMap.size(); n++) {
                String thisKey = n.toString();
                PaymentItem pItem = this.internalMap.get((thisKey ).toString());
                if (debug) System.out.println("Copy item key = " + thisKey );
                rtn.add((n - 1),pItem);
            }
            if (debug) System.out.println("Return allItems " + rtn.toString());
            return rtn;
        }catch(Exception e){
            rtn = null;
            System.out.println("Error in PaymentMap allItems " + e.toString());
            return rtn;
        }
    }

Now my repeat looks like this:

<xp:repeat id="repeat1" rows="30" var="pItem"
      indexVar="rIndex" value="#{javascript:payObj.allItems()}">
           <xp:text escape="true" id="computedField5"
               value="#{javascript:pItem.expPayDate}">
                     <xp:this.converter>
                            <xp:convertDateTime type="date"></xp:convertDateTime>
                      </xp:this.converter>
            </xp:text>
             <xp:br></xp:br>
 </xp:repeat>

Now in my this case the repeat gets three pItem elements and repeats the three lines, but instead of getting the date from each of the PaymentItem  it simply repeats the date from the last item:

displays

Aug 28, 2015

Aug 28, 2015

Aug 28, 2015

instead of

Sep 15, 2015

Sept 4, 2015

Aug 28, 2015

Very strange 

The log that prints from the allItems looks like this:

10/09/2015 10:01:44 AM  HTTP JVM: Copy item key = 1
10/09/2015 10:01:44 AM  HTTP JVM: Copy item key = 2
10/09/2015 10:01:44 AM  HTTP JVM: Copy item key = 3
10/09/2015 10:01:44 AM  HTTP JVM: Return allItems [ca.wfsystems.core.PaymentItem@4e664e66, ca.wfsystems.core.PaymentItem@52aa52aa, ca.wfsystems.core.PaymentItem@543a543a]

so the return allItems line looks correct and identifies the three different PaymentItem(s) and the pItem.expPayDate seems to be returning the correct field except rather than the value from three different Items it is getting the date from the last Item and displaying it 3 time. 

I can see how pItem becomes the 'datasource'  for my input control once I'm past this stumbling block.

I might need to move this to Stackoverflow.

 

 

Sep 10, 2015, 1:55 PM
589 Posts
hmm

Bill,

Please post all your Java code...  the full classes...

But I think you're problem is here:

 

  for (Integer n = 1; n <= internalMap.size(); n++) {
                String thisKey = n.toString();
                PaymentItem pItem = this.internalMap.get((thisKey ).toString());
                if (debug) System.out.println("Copy item key = " + thisKey );
                rtn.add((n - 1),pItem);
            }

 

you're in a loop...  and I'm not conviced you're correctly creating NEW paymentItem objects...  I don't get what you're doing with this internalMap stuff...  that's why I'd like to see all the java code...

Typically when I have a Java Item represent a document I have a load method to read from disk...

So for example..  if I'm in a "Company" object....  and I want to read into an internal map all my "Contacts"...  I'd have a java object for Company and a java object for each contact...  similar structure to what I think is your Order Object and lineItem Object.  I'm a little confused now on this paymentMap object.

 

 

so inside companyObject I have something like this:

 

private List<Contact> allContacts;

That's the internal variable... that would be loaded from disk when the company gets instantiated... or even lazy loaded...

 

private void loadContacts() {

  // here's where my loading loop would be

  .. get the session, db and view..., view Collection

  for (Document currentDoc : myViewCollection) {

     // Note: I can use for loops with the OpenNTF API.  if you're not using that do a typical while loop and get your next document manually.  don't forget to recycle

     Contact tempContact = new Contact();

     tempContact.load(currentDoc);

   this.allContacts.add(tempContact);   // If you want a map then something like   this.allContactsMap.put(stringKey, tempContact)

   

}

}

That's there rough just of what I do...  so I think you're missing the "new Contact()" piece to create a new object...  not positive though without seeing more code.

then my repeat would be:

companyObject.allContacts

and the "rowData" would now be the direct contact object...  which might have delete, save methods...  variables itself of course... etc...

 

 

 

 

Sep 10, 2015, 3:26 PM
453 Posts
here goes

I changed the allItems as follows:

    public ArrayList<PaymentItem> allItems(){
        ArrayList<PaymentItem> rtn = new ArrayList<PaymentItem>();
        try{
            for (Integer n = 1; n <= internalMap.size(); n++) {
                String thisKey = n.toString();
                PaymentItem pItem = new PaymentItem();
                pItem =  this.internalMap.get((thisKey ).toString());
                if (debug) System.out.println("Copy item key = " + pItem.getExpPayDate().toString() );
                rtn.add((n - 1),pItem);
            }
            for (Integer n = 0; n< rtn.size(); n++){
                if (debug) System.out.println("dates from ArrayList = " + rtn.get(n).getExpPayDate().toString());
            }
            if (debug) System.out.println("Return allItems " + rtn.toString());
            return rtn;
        }catch(Exception e){
            rtn = null;
            System.out.println("Error in PaymentMap allItems " + e.toString());
            return rtn;
        }
    }

and when it runs it prints the following:

10/09/2015 12:24:37 PM  HTTP JVM: Copy item key = Thu Sep 24 12:00:00 MDT 2015
10/09/2015 12:24:37 PM  HTTP JVM: Copy item key = Sat Sep 05 12:00:00 MDT 2015
10/09/2015 12:24:37 PM  HTTP JVM: Copy item key = Fri Aug 28 12:00:00 MDT 2015
10/09/2015 12:24:37 PM  HTTP JVM: dates from ArrayList = Thu Sep 24 12:00:00 MDT 2015
10/09/2015 12:24:37 PM  HTTP JVM: dates from ArrayList = Sat Sep 05 12:00:00 MDT 2015
10/09/2015 12:24:37 PM  HTTP JVM: dates from ArrayList = Fri Aug 28 12:00:00 MDT 2015
10/09/2015 12:24:37 PM  HTTP JVM: Return allItems [ca.wfsystems.core.PaymentItem@19741974, ca.wfsystems.core.PaymentItem@1cc81cc8, ca.wfsystems.core.PaymentItem@1f491f49]

so the ArrayList is populated with the correct dates and the issue seems to be with the repeat which repeats over the three PaymentItem(s) so it knows there are three item to repeat over, the ArrayList in payObj.allItems() is calculated correctly because in the printout in the allItems method it clearly has the correct three PaymentItems. The definition of the Value of the repeat would seem to work OK and pItem is of type PaymentItem, but ......

If I get the value using payObj,get(key).expPayDate I get the correct date, but if I use pItem.expPayDate I do not. I'm wondering if there is an issue with the PaymentItem get(key) in the paymentMap class that is causing a problem.

<xp:repeat id="repeat1" rows="30" var="pItem"
                            indexVar="rIndex" value="#{javascript:payObj.allItems()}">
                            <xp:text escape="true" id="computedField5"
                                value="#{javascript:pItem.expPayDate}">
                                <xp:this.converter>
                                    <xp:convertDateTime type="date"></xp:convertDateTime>
                                </xp:this.converter>
                            </xp:text>
                            <xp:br></xp:br>
 </xp:repeat>

 

Below is the PaymentMap Class which imports the PaymentItem Class. I have removed some of the methods from the PaymentMap that are not at issue here. The PaymentMap class is defined as a managed Bean called payObj. The process is that in the BeforePageLoad I call payObj.buildMap() which goes out to disk and reads N Notes Payment documents and stores the results in a HashMap<string,paymentMap>. I have a method payObj.saveItems() which is called in the QuerySave event of the mainDocument.  There are methods to create a new paymentItem and other things which are not in play with the current issue and are functioning in any case. 

So the paymentMap Class Looks like this:

package ca.wfsystems.core;

import javax.faces.context.FacesContext;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.ArrayList;
import java.util.Collections;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Date;

import java.math.BigDecimal;

import lotus.domino.Database;

import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;

import lotus.domino.DateTime;
import lotus.domino.Document;

import com.ibm.xsp.extlib.util.ExtLibUtil;

import ca.wfsystems.core.PaymentItem;
import ca.wfsystems.core.ApplicationMap;
import ca.wfsystems.core.AppProperties;
import ca.wfsystems.core.Utils;

public class PaymentMap implements Serializable, Map<String, PaymentItem> {

    private static final long serialVersionUID = 1L;

    private Boolean debug = true;

    private Map<String, PaymentItem> internalMap = new HashMap<String, PaymentItem>();

    public PaymentMap() {

    }

    public String newItem(String lKey, Date expPayDate) {
        try {
            if (debug)
                System.out.println("In New Item");
            Integer n = (internalMap.size() + 1);
            String key = n.toString();
            PaymentItem pItem = new PaymentItem();
            pItem.setChanged(true);
            pItem.setUnid(lKey);
            pItem.setPaymentID("");
            pItem.setExpPayDate(expPayDate);
            BigDecimal bd = new BigDecimal("0");
            pItem.setPayAmount(bd);
            internalMap.put(key, pItem);
            this.reBuildMap();
            return key;
        } catch (Exception e) {
            System.out.println("Error in newItem " + e.toString());
            return null;
        }
    }

  

    public ArrayList<PaymentItem> allItems(){
        ArrayList<PaymentItem> rtn = new ArrayList<PaymentItem>();
        try{
            for (Integer n = 1; n <= internalMap.size(); n++) {
                String thisKey = n.toString();
                PaymentItem pItem = new PaymentItem();
                pItem =  this.internalMap.get((thisKey ).toString());
                if (debug) System.out.println("Copy item key = " + pItem.getExpPayDate().toString() );
                rtn.add((n - 1),pItem);
            }
            for (Integer n = 0; n< rtn.size(); n++){
                if (debug) System.out.println("dates from ArrayList = " + rtn.get(n).getExpPayDate().toString());
            }
            if (debug) System.out.println("Return allItems " + rtn.toString());
            return rtn;
        }catch(Exception e){
            rtn = null;
            System.out.println("Error in PaymentMap allItems " + e.toString());
            return rtn;
        }
    }
    
   

    public void buildMap(String lKey) {

        try {
            if (debug)
                System.out.println("In buildMap of PaymentMap");

            if (debug)
                System.out.println("Map Updated");
            Map sessionscope = (Map) ExtLibUtil.resolveVariable(FacesContext
                    .getCurrentInstance(), "sessionScope");
            String app = sessionscope.get("ssApplication").toString();
            if (debug)
                System.out.println("Building Payment Map " + app);
            ApplicationMap appProps = (ApplicationMap) ExtLibUtil
                    .resolveVariable(FacesContext.getCurrentInstance(),
                            "appProps");
            AppProperties thisApp = appProps.get(app);
            Database appDB = thisApp.getAppDB();
            if (debug)
                System.out.println("Got DB " + appDB.getTitle());
            View vwPayment = appDB.getView("vwPaymentsByLinkKey");
            ViewEntryCollection veCol = vwPayment.getAllEntriesByKey(lKey);

            if (debug)
                System.out.println("Got Collection " + veCol.getCount());
            if (veCol.getCount() == 0) {
                // No matching Documents
                if (debug)
                    System.out.println("No Matches Found ");
                return;
            } else {

                for (int n = 1; n <= veCol.getCount(); n++) {
                    if (debug)
                        System.out.println("in loop n = " + n);
                    ViewEntry ve = veCol.getNthEntry(n);
                    Document pDoc = ve.getDocument();
                    PaymentItem pItem = new PaymentItem();
                    pItem.setUnid(pDoc.getUniversalID());
                    pItem.setChanged(false);
                    // String key = s.evaluate("@Unique").get(0).toString();

                    // ExpPayDate
                    try {
                        DateTime dt = (DateTime) pDoc
                                .getItemValueDateTimeArray("ExpPayDate").get(0);
                        Date pDate = dt.toJavaDate();
                        pItem.setExpPayDate(pDate);
                    } catch (Exception e) {
                        // date error
                    }
                    // ActualPayDate
                    try {
                        if (debug)
                            System.out.println("Get ActualPayDate ");
                        DateTime dt = (DateTime) pDoc
                                .getItemValueDateTimeArray("actualPayDate")
                                .get(0);
                        Date pDate = dt.toJavaDate();
                        if (debug)
                            System.out.println("ActualPayDate = "
                                    + pDate.toString());
                        pItem.setActualPayDate(pDate);
                        if (debug)
                            System.out.println("Set ActualPayDate ");
                    } catch (Exception e) {
                        // date error
                        if (debug)
                            System.out.println("ActualPayDate = null");
                    }
                    // Payment Amount
                    try {

                        String amt = pDoc.getItemValue("Amount").get(0)
                                .toString();
                        if (debug)
                            System.out.println("Get Amount " + amt);
                        BigDecimal bd = new BigDecimal(amt);
                        pItem.setPayAmount(bd);
                        if (debug)
                            System.out.println("Set Amount " + bd.toString());
                    } catch (Exception e) {
                        // amount = null
                        BigDecimal bd = new BigDecimal("0");
                        pItem.setPayAmount(bd);
                        if (debug)
                            System.out.println("Set Amount " + bd.toString());
                    }
                    pItem.setPaymentID(pDoc.getItemValueString("PaymentID"));

                    if (debug)
                        System.out.println("Update Internal Map");
                    String key = Integer.toString(n);
                    internalMap.put(key, pItem);
                    Utils.recycleObjects(pDoc, pItem, ve);
                }// for loop
            } // else main process

            Utils.recycleObjects(appDB, vwPayment, veCol);
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }

    public boolean containsKey(Object key) {
        return this.internalMap.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return this.internalMap.containsValue(value);
    }

    public Set<java.util.Map.Entry<String, PaymentItem>> entrySet() {
        return this.internalMap.entrySet();
    }

    public PaymentItem get(Object key) {
        try {
            if (this.internalMap.containsKey(key)) {
                if (debug)
                    System.out.println("Got internalMap");
                return this.internalMap.get(key);

            } else {
                if (debug)
                    System.out.println("No Key in PaymentMap " + key);
                PaymentItem pItem = new PaymentItem();
                return pItem;

            }

        } catch (Exception e) {
            System.out.println("PaymentMap" + e.toString());
            PaymentItem pItem = new PaymentItem();
            return pItem;
        }

    }

    public boolean isEmpty() {
        return this.internalMap.isEmpty();
    }

    public Set<String> keySet() {
        return this.internalMap.keySet();
    }

    public PaymentItem put(String key, PaymentItem value) {
        return this.internalMap.put(key, value);
    }

    public void putAll(Map<? extends String, ? extends PaymentItem> m) {
        this.internalMap.putAll(m);
    }

    public PaymentItem remove(Object key) {
        return this.internalMap.remove(key);
    }

    public int size() {
        return this.internalMap.size();
    }

    public Collection<PaymentItem> values() {
        return this.internalMap.values();
    }

    public void clear() {
        // TODO Auto-generated method stub

    }

}

 

The PaymentItem Class is as follows:

 

package ca.wfsystems.core;


import java.io.Serializable;

import java.util.Date;
import java.math.BigDecimal;


public class PaymentItem implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private Boolean debug = true;
    
    public PaymentItem() {};

    private Date expPayDate;
    
    private Date actualPayDate;
    
    private Number payAmount;
    
    private String paymentID;
    
    private Boolean changed;
    
    private String unid;

    public Date getExpPayDate() {
        return expPayDate;
    }

    public void setExpPayDate(Date expPayDate) {
        this.expPayDate = expPayDate;
    }

    public Date getActualPayDate() {
        return actualPayDate;
    }

    public void setActualPayDate(Date actualPayDate) {
        this.actualPayDate = actualPayDate;
    }

    public Number getPayAmount() {
        return payAmount;
    }

    public void setPayAmount(BigDecimal payAmount) {
        this.payAmount = payAmount;
    }

    public String getPaymentID() {
        return paymentID;
    }

    public void setPaymentID(String paymentID) {
        this.paymentID = paymentID;
    }

    public Boolean getChanged() {
        return changed;
    }

    public void setChanged(Boolean changed) {
        this.changed = changed;
    }

    public void setUnid(String unid) {
        this.unid = unid;
    }

    public String getUnid() {
        System.out.println("Get UNID = " + unid);
        return unid;
    }
    
}

Sep 10, 2015, 5:11 PM
453 Posts
Now I'm totally confused ...

Beating ones head against the wall feels so good when you stop.

In any case without making any chnage to the code except to bind the computedText field using EL rather than JS pItem.expPayDate in both csaes it started to work correctly. Changed it back to JS and it started to fail all three dates were the same. Changed it back to EL and it still failed. deleted the field from the source and recreated it with EL and now it works. I have no idea why it would work in EL but not JS. in any case I will now move on to my next step and see if it blows up.

Thanks Dave and Howard for your insights.

Sep 11, 2015, 7:48 AM
589 Posts
hmmm

Bill,

I looked at this briefly...  first regarding your most recent comment of the difference between javaScript and EL..

<xp:repeat id="repeat1" rows="30" var="pItem"
                            indexVar="rIndex" value="#{javascript:payObj.allItems()}">
                            <xp:text escape="true" id="computedField5"
                                value="#{javascript:pItem.expPayDate}">
                                <xp:this.converter>
                                    <xp:convertDateTime type="date"></xp:convertDateTime>
                                </xp:this.converter>
                            </xp:text>
                            <xp:br></xp:br>
 </xp:repeat>

I assume of course you're talking about this computed text here...  honestly I'm surprised that did anything in SSJS...  It's not right...  it should have been:

pItem.getExpPayDate() 

since yourmethod is:  

 public Date getExpPayDate() {
        return expPayDate;
    }

SSJS you need to use the "get" and the "()"

in EL it will assume the get and set for you and you don't need the () of course...  So I would expect that method to work fine via EL...

 

If that solves your problem then great..

Just a couple random comments on your code...  This is more stylistically so feel free to ignore of course... You're doing some things that I've not seen before so you're way above me in some areas...

I almost never use "Boolean"...  it's always "boolean" for me.... the only time I ever use "Boolean" is if a null value is a possibility.  Meaning the user might not have chosen a value yet.  the other 98% of the time I use "boolean" because that will always have a value - true or false.  Boolean can be true, false, null.

In your PaymentMap - you've implemented the Map interface...  That's also something I typically don't do...  I wonder if that's added some complexity to your object...

I would probably call my object "Payment" or whatever... and I'd have an internal Map of the Items inside... just like you do...  and I'd have a getter to get the map etc...  I'm not seeing an advantage of implementing the Map class itself in this case when you store the Map inside and can easily get to that directly.

If your object is called "payment" and it contains an "itemMap" you can access it easily enough :

payment.getItemMap().get("myKey") 

payment.getItemMap().getValues()..  etc.  

Stuff like that...  I think that's much simpler in this case them implementing the Map interface directly...

And speaking of Map...  do you really even need it to be a Map?  You're using and converting to a List anyway...  This is going to a repeat control...  so the list is outputting PaymentItem objects...  So unless you need to directly pull some item by a key from your PaymentMap - which I'm not seeing yet...  you might be better off with an internal list instead of a Map.

your method "allItems()" should probably be called "getAllItems()"..  then you can use it in EL easier and it's more along the standards...

" rtn.add((n - 1),pItem);"

I've never done that at all...  wow.  didn't know you could..  what are you doing there?  trying to sort the list?  is order important?  Usually when I convert a Map to a List I do something like:

 ArrayList<PaymentItem> rtn = new ArrayList<PaymentItem>(this.internalMap.values());

If sorting is important...  then in my object I'll implement the Comparable/Comarator stuff..  http://www.mkyong.com/java/java-object-sorting-example-comparable-and-comparator/

Then I can just do a Collections.sort....  or use a TreeSet which will auto sort as you add the items...

in your buildMap() function...

I HIGHLY recommend you use a while loop instead...  getNthEntry calls in loops are typically very inefficient...  same as in LotusScript...  Even Bob Balaban who created the original document object model said he regretted creating that method...  I think there is 1 or 2 small use cases where it's needed...  but typically you are much better off with a while.  Humbly suggest these loops look more like this:

while (doc != null) {
tmpDoc = collect.getNextDocument();

// Insert Code here. Work with the “doc” object. doc.recycle();
doc = tmpDoc;

(could be viewEntries of course)

 

You're using viewEntries -  ViewEntryCollection veCol = vwPayment.getAllEntriesByKey(lKey);

but then immediately converting to a document -  Document pDoc = ve.getDocument();

In theory using a ViewEntryCollection is faster... but getting the doc is expensive...  so if you are not going to populate your item via columnValues... then you might as well just use a documentCollection instead of a viewEntryCollection...  

you're doing a good job recycling....  but I believe NotesDate objects are special...  they actually spawn from the Session...  I don't think recycling the doc kills them.  Paul Withers is the expert there...  but you might want to recycle them specifically...  there's also an issue if there's a date in ColumnValues....  I forget..  

Anyway that's just my 2 cents...  

Hope something might be helpful.

 

Dave

 

Sep 11, 2015, 11:22 AM
453 Posts
Some of the structure is because ...

1. Copy and paste from other examples does not always result in the best solution. The Map issue is probably one of those .. that structure is the result of some feed back I got on StackOverflow that kind of got me started on a similar class I built. My understanding of all of the implications of JAVA objects is somewhat limited, so in many cases it is very possible that it works but is not the most efficient. 

2. I know that the get Nth item is the result of some issues that I was experiencing earlier in the development process and I know I need to go back and change that all to a while loop.

3. I have seen enough issues as I have put this together that my objective at this point is to get something that works, then I'm seriously considering starting the process over and rebuild the whole thing and clean up and optimize much of the code based on what I have learned in this process. Almost 20 years of Native Notes Development can be hard to shack.

3. Most of all i appreciate yours, and others, comments and observations. This whole JAVA thing has been a major stretch for my old mind, but the more I use it the more I see applications for it. 

I have looked at my class for this app and using the Map in this case is really not necessary. I'm going to rewrite the class so that it is an ArrayList<PaymentItem> which resolves many of my other issues.

Edit ---- 

I have decided to back up a bit and start the process over by redefining the Payments object to be a Collection, the Hash Map really just complicated the issue of retrieving the items. My class definition now looks like this:

 

public class Payments implements Serializable, Collection<PaymentItem> {

    private static final long serialVersionUID = 1L;

    private Boolean debug = true;
    
    private ArrayList<PaymentItem> allItems;

    private Collection<PaymentItem> internalArray = new ArrayList<PaymentItem>();

Have not got all of it working yet but it is sure a lot cleaner.

Bill

Sep 11, 2015, 1:45 PM
300 Posts
I found this explanation by Stepan Wissel to be great
He explains how to create a managed bean and then bind to properties using SSJS and EL. What is nice is that his getValue returns an object. So, depending on what you are getting I have used his approach to return a String or a ArrayList, etc. depending on what the data was.
http://www.wissel.net/blog/d6plinks/SHWL-8CZ5UZ


Howard
Sep 11, 2015, 5:20 PM
453 Posts
Thank Howard

I will check that out.

Sep 22, 2015, 9:37 AM
38 Posts
a small comment

When I look at your Java code, don't go the for(Integer i = 1....... way. If you loop, it is a primitive int and not an object Integer

But... that loop could be easier

for(PaymentItem payItem : internalMaps.values()){

}

 


FORUM PLAN UPDATE
Date revision: This forum will remain open to new posts and responses until December 1, 2018. (After that date, you will still be able to view and search the forum.) Also, we're taking a second look at the best place to host future conversation. For now, keep using this forum, and stay tuned for more news.