NotesItemWrapper

'*** Constants ***
Private Const ITEM_MAX_SIZE = 31000


Public Class NotesItemWrapper
	'declarations
	Private pDoc As NotesDocument
	Private pCurrentItem As NotesItem
	Private pItemNames As Variant
	Private pItemIndex As Integer

	'/**
	' * Constructor.
	' */
	Public Sub New(doc As NotesDocument, item_names As Variant, append_data As Boolean)
		'declarations
		Dim i As Integer

		'store the document
		Set Me.pDoc = doc

		'item_names must be an array of strings or a item
		Forall n In item_names
			If Typename(n)  "STRING" Then
				Error 9999, "The supplied item names but be a string or an array of strings."
			End If
		End Forall

		'make sure the item names are an array
		If Not Isarray(item_names) Then
			Dim v(0) As String
			v(0) = Cstr(item_names)
			Me.pItemNames = v
		Else
			'store the item names to use
			Me.pItemNames = item_names
		End If

		'get the first item to use
		If Not append_data Then
			'remove all the items with the supplied names and recreate the first one
			Forall n In Me.pItemNames
				Call Me.pDoc.RemoveItem(n)
			End Forall

			'recreate the first item
			Set Me.pCurrentItem = New NotesItem(Me.pDoc, Me.pItemNames(Me.pItemIndex), "")
		Else
			'find the first item to use
			For i=0 To Ubound(Me.pItemNames)
				'get the first item
				Set Me.pCurrentItem = Me.pDoc.GetFirstItem(Me.pItemNames(Me.pItemIndex))

				'is this item below the threshold
				If Lenb(Me.pCurrentItem.Text) < ITEM_MAX_SIZE Then
					'use this item
					Me.pItemIndex = i
					Exit For
				End If
			Next
		End If
	End Sub

	'/**
	' * Appends text to the item checking whether a new item
	' * is required to hold the text. If no more fields are
	' * available we return an Error 9999.
	' */
	Public Sub AppendText(data As String)
		'check whether there is space for the data
		If Me.pCheckSize(data) Then
			'it is ok - append
			Me.pCurrentItem.Text = Me.pCurrentItem.Text + data
		Else
			'raise error
			Error 9999, "Not enought space to hold the data."
		End If
	End Sub

	'/**
	' * Appends a multi-value item to the item(s). If no more space
	' * is available we return an Error 9999.
	' */
	Public Sub AppendToTextList(data As String)
		'check whether there is space for the data
		If Me.pCheckSize(data) Then
			'it is ok - append
			Call Me.pCurrentItem.AppendToTextList(data)
		Else
			'raise error
			Error 9999, "Not enought space to hold the data."
		End If
	End Sub

	'*****************************************************************************
	'* Private methods
	'*****************************************************************************

	'/**
	' * Checks the size of the current item. Returns True if the item
	' * can hold the supplied data.
	' */
	Private Function pCheckSize(data As String) As Boolean
		'declarations
		Dim size As Long

		'calculate the item size
		size = Lenb(Me.pCurrentItem.Text) + Lenb(data)
		'Print "Size is: " & size & " (index = " & Me.pItemIndex & ", " & Me.pCurrentItem.Name & ")"

		'decide what to do
		If size < ITEM_MAX_SIZE Then
			'item can hold the byte count
			pCheckSize = True
		Else
			'no can do - try and get the next item
			If Me.pItemIndex <= Ubound(Me.pItemNames) Then
				'get the next item
				Me.pItemIndex = Me.pItemIndex + 1
				Set Me.pCurrentItem = New NotesItem(Me.pDoc, Me.pItemNames(Me.pItemIndex), "")

				'return true
				pCheckSize = True
			Else
				'no more items
				pCheckSize = False
			End If
		End If
	End Function

End Class

Thought no one was using mod_jk2 anymore…

…but I appear to be mistaken. I am very confused about the availability and the differences between the different mod_jk
* modules. I will have to look into it.

This article explains how to use mod_jk2 under Apache to cluster multiple Tomcat instances behind an Apache web server.

StringTokenizer

I just can’t believe this kind of functionality isn’t built in but so it is. The code should be pretty straight forward and well commented.

A StringTokenizer is used to split a string of text up into smaller bits delimeted by the chosen delimeter. A StringTokenizer really shines when it comes to parsing comma-separated files or the like.

Feel free to use the code but please let me know if you do. The code is provided as-is and I accept no responsibility what so ever.

'*** Constants ***
Private Const STANDARD_DELIMETER$ = ";"

'/**
' * StringTokenizer class to help break a string into tokens. A token is the
' * string between delimeters (defaults to semi-colon). The class handles
' * lines ending in a delimeter if you set the EndingDelimeter property
' * to True.
' *
' * Standard usage:
' * 1. Dim a new StringTokenizer using the source to parse as an argument.
' * 2. (Optional) Change the delimeter using the Delimter property.
' * 3. (Optional) Change the EndingDelimeter property.
' * 4. Call the HasNextElement() method and the NextElement() functions to
' *    loop the string.
' *
' * Example 1:
' * Dim t1 As New StringTokenizer(";123;456;789;")
' * While t1.HasNextElement()
' *   Print t1.NextElement()
' * Wend
' *
' * Output:
' *  - 
' *  - 123
' *  - 456
' *  - 789
' *  - 
' *
' * Example 2:
' * Dim t2 As New StringTokenizer(";123;456;789;")
' * t2.EndingDelimeter = True
' * While t2.HasNextElement()
' *   Print t2.NextElement()
' * Wend
' *
' * Output:
' *  - 
' *  - 123
' *  - 456
' *  - 789
' *
' * Example 3:
' * Dim t3 As New StringTokenizer("123;456%789;012")
' * t3.Delimeter = "%"
' * While t3.HasNextElement()
' *   Print t3.NextElement()
' * Wend
' *
' * Output:
' *  - 123;456
' *  - 789;012
' *
' * @version 1.0 (16 December 2004)
' * @author lekkim@it-inspiration.dk
' * @author it-inspiration aps
' */
Public Class StringTokenizer
   'declarations
   Private pSource As String
   Private pDelimeter As String
   Private pStart As Long
   Private pDelim As Long
   Private pEndingDelimeter As Boolean

   Public Sub New(source As String)
      Me.pSource = source
      Me.pStart = 0
      Me.pDelim = 0
      Me.pEndingDelimeter = False
      Me.pDelimeter = STANDARD_DELIMETER
   End Sub

   Public Property Set EndingDelimeter As Boolean
      Me.pEndingDelimeter = EndingDelimeter
   End Property

   Public Property Set Delimeter As String
      Me.pDelimeter = Left(Delimeter, 1)
   End Property

   Public Function HasNextElement() As Boolean
      'declarations
      Dim index As Long

      'does the source have contents
      If Len(Me.pSource) = 0 Then
         'nope - return false
         HasNextElement = False
         Exit Function
      End If

      'have we been looking for a delimeter before ?
      If Me.pStart = 0 And Me.pDelim = 0 Then
         'nope - does the source start with a delimeter ?
         If Left(Me.pSource, 1) = Me.pDelimeter Then
            'the first element is empty
            Me.pDelim = 1

            'set start
            Me.pStart = 1
         Else
            'find the first delimeter
            index = Instr(1, Me.pSource, Me.pDelimeter)

            'did we find a delimeter
            If index > 1 Then
               'yes we did - set the index of the delimeter
               Me.pDelim = index

               'set start
               Me.pStart = 1
            Else
               'no we didn't only one element
               Me.pDelim = 0
               Me.pStart = 1
            End If
         End If

         'return true
         HasNextElement = True
         Exit Function
      Else
         'we have been looking before - move the pointers
         Me.pStart = Me.pDelim + 1

         'have we reached the end of the source string ?
         If Me.pStart > Len(Me.pSource) Then
            'yes we have - return false
            HasNextElement = False
            Exit Function
         Elseif Me.pStart = Len(Me.pSource) Then
            'see if the last character is a delimeter
            If Right(Me.pSource, 1) = Me.pDelimeter Then
               'the last character is a delimeter - should the class add
               'an empty element at the end
               If Me.pEndingDelimeter Then
                  'the lines end in a delimeter
                  HasNextElement = False
               Else
                  'we should signal an empty element
                  Me.pDelim = Me.pStart
                  HasNextElement = True
               End If
            Else
               'the is a one character element at the end
               Me.pStart = Len(Me.pSource)
               Me.pDelim = 0
               HasNextElement = True
            End If
         Else
            'just look for the next delimeter
            index = Instr(Me.pStart, Me.pSource, Me.pDelimeter)

            'did we find a delimeter
            If index > Me.pStart Then
               'we found a delimter
               Me.pDelim = index
               HasNextElement = True
            Elseif index = 0 And Me.pStart < Len(Me.pSource) Then
               'there is one more element
               Me.pDelim = Len(Me.pSource)
               HasNextElement = True
            Else
               'no more delimeters
               HasNextElement = False
            End If
         End If
      End If
   End Function

   Public Function NextElement() As String

      'if the delimeter is 0 there is only one element to return
      If Me.pDelim = 0 Then
         NextElement = Mid(Me.pSource, Me.pStart)
         Exit Function
      Elseif Me.pDelim = Me.pStart Then
         'return an empty element
         NextElement = ""
         Exit Function
      Elseif Me.pDelim = Len(Me.pSource) Then
         'does the source end with a delimeter
         If Me.pEndingDelimeter Then
            'the line is supposed to end with a delimeter so we remove it
            NextElement = Mid(Me.pSource, Me.pStart, Len(Me.pSource) - Me.pStart)
         Else
            'the line should not end with a delimeter so if it does so we need to
            'return the rest of the line minus 1
            If Right(Me.pSource, 1) = Me.pDelimeter Then
               NextElement = Mid(Me.pSource, Me.pStart, Len(Me.pSource) - Me.pStart)
               Me.pDelim = Me.pDelim - 1
            Else
               NextElement = Mid(Me.pSource, Me.pStart)
            End If
         End If
      Else
         NextElement = Mid(Me.pSource, Me.pStart, Me.pDelim-Me.pStart)
      End If

   End Function

End Class

Google suggests

Google suggests looks to me to what we really need in our application. in this particular application the user has to select a street from the database without resorting to dropdowns and the like. At the moment this is a accomplished by the user specifying the start of the street name, clicks a button (or presses enter) and a asynchronenous XML query is run the background. This works perfectly but having something like Google suggests would surely help users.

The main issue with the way it currently works is that users need to know the exact spelling of the street (or at least parts of the exact spelling) to find anything. Using something like Google suggests has users could have some help finding the street without needing to know the spelling.

This guy dissected the code and made it more readable.

Thinking about LotusScript

Lately I have been help thinking about the LotusScript language and why it hasn’t matured more over the years. I have two primary grievances:

  • The lack of OO support in the IDE.
  • The lack of helper classes in the API.

The lack of OO support in the IDE

Come on Lotus – when will this happen. How come it is practically easier to develop LotusScript classes in Visual Studio than in Domino Designer. Please step up and add class recognizion to the LotusScript IDE. It is practially imposible to handle classes in Script Libraries and in Agents.

What would be really help would be a simple tree structure showing the class hierarchy in the left hand side – it anything else fails let us have the same UI as in the Java IDE… 🙂

I think it is a real show stopper for the adaption of OO design and programming since programmers new to OOP loose track of the code and the structure. The overview is much better when you use the traditional procedural approach.

The lack of helper classes in the API

I know additional classes has been added in version 6 but how come no collection api or more general string operation classes has been added.

There is a major need for a standardized collection API to help the adoption of OOP. Just see what the Collection API did for Java 2.

AMgr memory consumption part 2

After talking to Lotus Support and monitoring the server more closely it appears not to be the AMgr alone having the problem. The problem appears to be more general. Yesterday afternoon the CA task was at 200 MB memory! An important task but not one one of the busiest I must say…

Number of Lotus Notes seats

Hi Mikkel --

Both vendors have generally gotten away from making
seat counts the focus.  I think Microsoft is claiming
130 million, and IBM claims about 113 million.  The
problem with seat counts is that they are completely
artificial... unaudited numbers.

In terms of numbers to bet on, you could do well with
the Gartner market share numbers.  They report IBM as
having 46% share, Microsoft at 44.2%.  These
percentages are based on revenue, not seats.
Those numbers are reported publicly at
http://www.cmpnetasia.com/ViewArt.cfm?Artid=24452&Catid=8&subcat=83

Hope this helps.

--Ed