LotusScript.doc Release Candidate 1

I just shot of an e-mail with LotusScript.doc release candidate 1 to the “beta” testers a few moments ago. This will hopefully be the last release for general release – changelog below.

Fixes

  • Make sure code isn’t parsed inside %REM-sections to avoid parse errors due to invalid code inside %REM-sections (Julian Buss).
  • Make sure the implicit datatype of Variant for arguments is set if an argument to a sub or function doesn’t have a datatype implicitly specified.
  • Fix issue with return type in functions having an array as an argument.

Improvements

  • Move all constants into “CLASS: LsDocConstants” script library (Alain Romedenne).
  • Make it possible to write the documentation block inside the function or sub if the function or sub is not inside a class. This allows you to keep the comment with the function or sub when copy/pasting code (Thomas Dufner).
  • Added sorting of method/function/property names in class detail pages.
  • Updated documentation.
  • Performance improvements when generating documentation in the background on the server.
  • Support for only outputting documented types (classes, subs, functions) in HTML documentation.
  • Support for ignoring private members when outputting the documentation.

I know there are some outstanding feature requests but I would rather get the tool out now than keep adding new features. I hope for your understanding.

Implementing a Bag in LotusScript

A Bag is a collection that keeps track of the number of times each element occurs in the collection. The name was inspired by the Bag implementation from the Apache Jakarta Commons Collections.

Please note that this is a quick implementation and it only handles simple datatypes – no support for objects at present.

The code is of cause documented using LotusScript.doc… 🙂

'/**
' * Implementation of a bag that is a collection that keeps track of the
' * number of times an element occurs in a collection.
' */
Public Class Bag
   'declarations
   Private pElements As Vector
   Private pElementCount List As Integer

   '/**
   ' * Constructor.
   ' */
   Public Sub New()
      Set Me.pElements = New Vector()
   End Sub

   '/**
   ' * Adds an element to the bag.
   ' * @param e The element to add.
   ' */
   Public Sub AddElement(e As Variant)
      'declarations
      Dim hash_e As Variant

      'add the element to the vector
      Call Me.pElements.AddElement(e)

      'do we have a count for this already
      If Not Iselement(Me.pElementCount(e)) Then
         'we do not have a count
         Me.pElementCount(e) = 1
      Else
         Me.pElementCount(e) = Me.pElementCount(e) + 1
      End If
   End Sub

   '/**
   ' * Get a count of the number of times an element occurs in the bag.
   ' *
   ' * @param e The element for look for.
   ' * @return Count of the times the element occurs (0 if it doesn't occur)
   ' */
   Public Function GetCount(e As Variant) As Long
      'declarations
      Dim hash_e As Variant
      If Not Iselement(Me.pElementCount(e)) Then
         GetCount = 0
      Else
         GetCount = Me.pElementCount(e)
      End If
   End Function

   '/**
   ' * Returns an array of the unique elements added.
   ' *
   ' * @return Array of unique elements added.
   ' */
   Public Function GetUnique() As Variant
      Dim v() As Variant
      Dim i As Integer
      Forall k In Me.pElementCount
         Redim Preserve v(i)
         v(i) = Listtag(k)
         i = i + 1
      End Forall
      GetUnique = v
   End Function

End Class

Small fix to Johans Vector class

Small fix to the removeElementAt(Integer) method – changes in bold.

Public Function removeElementAt(index As Integer)
   If index >= Me.size() Then Error 2000, "Array index [" & index & "] out of bounds [" & size & "]"
   Dim members As Variant
   Dim i As Integer
   Dim j As Integer
   Redim members(Me.size())
   j=0
   For i = 0 To Me.size-1
      If (i  index) And (Not j > Me.size()) Then
         If Isobject(array(i)) Then
            Set members(j) = array(i)
         Else
            members(j) = array(i)
         End If
         j = j + 1
      End If
   Next i
   elementLength = elementLength - 1
   array = members
End Function

Programmatically detecting AutoSave

The below excerpt is from the article “All about AutoSave in Lotus Notes/Domino 7” on Lotus Developer Domain:


Only documents created from forms with Allow AutoSave selected can be autosaved. This is the only way to ensure that the document can be recovered after it has been autosaved. Many Notes/Domino applications have code in their Save and Post Save events that create items, or perform validations that are checked at load time. Because we can’t execute these events while autosaving the document, none of this will take place, and the autosaved document will be in a “bad” state. This could lead to failing to load the document after recovery.


If your application does write items on save events, and it’s dependant upon these item, you may need to add code in your Query Open event to put defaults for those items. You can even do special handling only for documents that are recovered by looking for an item called $AutoSaveRecovered. This item will only be available for recovered documents up until the Post Open event, and will be deleted after that.


Another way to programmatically disable AutoSave on individual documents is by adding the item $DontAutosave. This item prevents AutoSave on that document. If you remove $DontAutosave, the document can again be autosaved.


NOTE: In Notes/Domino 7.0, the only shipping template form that is enabled for AutoSave is the Memo form.

Nice to know that fields has been added to detect whether a document is being recovered via AutoSave.

Sorting document collections in LotusScript

Another option to the function in NotesDatabase was to use the FTSearch() method in NotesView but this method behaves different depending on whether the database is full-text indexed or not. Apparently the sorting of the view is only honored for the search results if the database is NOT full-text indexed. Why this distinction? Why not give me the option?

Anyways… The solution was to use the FTSearch() method in NotesDatabase as I started out doing and then using a function I found via Lotus Developer Domain that can sort a NotesDocumentCollection based on field names.

The function is written by Max Flodén and is available from http://www.tjitjing.com.

Force Domino to output XHTML to allow parsing as XML

In an application I did recently I needed to easily be able to fetch valid XML documents for a lot of documents from the Domino application. This isn’t really a challenge you will say and you’re right… The challenge was that I would like to reuse the HTML that Domino would generate for me for rich text fields etc.

This is a problem since the HTML Domino generates isn’t XHTML so I would get an error when supplying the retrieved page to my JavaScript XML parser. The browser would also complain that the page wasn’t valid XML.

The solution was an undocumented Domino URL argument. When adding the URL argument &OutputFormat=xhtml to the URL Domino will generate valid XHTML and this solved my problem.

Now I could have my cake and eat it too… Now the retrieved page could be supplied to my XML parser and the “embedded” HTML generated by Domino could be extracted and easily shown in a <div> tag using JavaScript. Sweet.

The documents are looked up through a special view using a form formula to make sure the documents are displayed using my special form. The entire form is passthrough HTML and all the contents is wrapped in a CDATA section to be safe.

Using this approach I could reuse the Domino HTML for rich text fields and easily extend the presentation.

Using Regular Expressions from LotusScript

Visual Basic Script (VBScript) version 5 and later (current version is 5.6) has a very powerful RegExp engine that is accessible through COM. The engine supports all the bells and whistles of regular expressions incl. grouping, backreferences etc.

Having regular expressions available from LotusScript is great since it allows you to use it for validation on the client etc. Another great thing is that the VBScript component is installed by default (at least on Windows XP) so there’s no need to touch client machines.

The VBScript RegExp engine is accessed from LotusScript using COM using the CreateObject() method:

Dim regexp As Variant
Set regexp = CreateObject("VBScript.RegExp")

Once you have a RegExp object you will normally use a code snippet like the following to do simple pattern matching:

'declarations
Dim regexp As Variant
Dim rc As Integer

'create object
Set regexp = CreateObject("VBScript.RegExp")

'make pattern matching case insensitive
regexp.IgnoreCase = True

'set pattern
regexp.Pattern = |[1-9]+[0-9]* Failed|

'test pattern
rc = regexp.Test(|Backup job XYZ: 2 Failed, 0 Succeeded.|)
If rc = -1 Then
   MsgBox "Match - backup failed..."
Else
   MsgBox "No match - backup suceeded..."
End If

Further reading