Unable to access Items array in NotesDocument

I am having trouble accessing the NotesDocument.Items array, and I can’t figure out why.Here is the situation: We have a database that allows users to embed jpg files into rich text fields by the name of PageBody. This database routinely gets too large for the server, and the users don’t have time to go back and remove old pictures. We want to have a script that searches through the documents in the database and removes only the jpg files, but leaves the document itself in place.

The script that I wrote yesterday was working fairly well (this was a test script, and does not yet have the “Remove” method), but would occassionally throw a “Type Mismatch” on the first Forall loop. When I looked at the document properties in debugger, “Items” did not have a twistie, so my assumption was that for whatever reason I was not allowed to access the items array for that particular document. Since having a few docs “slip by” this agent wouldn’t be a problem, I added the IsArray test so that I could simply bypass the docs that would throw this error.

The problem I have now, though, is that none of the documents in the database will allow my script to see the Items array, even when I confirm that I can see the items in a document properties window using the same ID.

Can anyone help with this issue? (Note: some of the variables in the script are just placeholders for some breaks in debugger.)

Sub Initialize

Dim session As New NotesSession

Dim db As NotesDatabase

Set db = session.GetDatabase("xxxx", False)

Dim dc As NotesDocumentCollection

Set dc = db.AllDocuments

Dim doc As NotesDocument

Set doc = dc.GetFirstDocument

Dim Oname As String

Dim test As String

Dim	success As String

Dim counter As Integer

counter = 0

Dim fSize As Long

fSize = 0

Dim rt As NotesRichTextItem

Dim eo As NotesEmbeddedObject

Dim crdate As String

Dim nowdate As String

nowdate = Cstr(Now)

Dim age As Integer

Dim itemcount As Integer

	

While Not (doc Is Nothing)	

	If doc.HasItem("PageBody")  Then

		If Isarray(doc.Items) Then

			crdate = Cstr(doc.Created)

			age = Datevalue(nowdate) - Datevalue(crdate)

						

			If age > 150 Then

				'items = doc.items

				If doc.HasEmbedded Then						

					Forall i In doc.items

						If i.name = "PageBody" Then

							Set rt = i

							Forall e In rt.EmbeddedObjects

								oName = e.Name

								test = Strright(oName, ".")

								If test = "JPG" Then

									success = "True"

									counter = counter + 1

									fSize = fSize + e.FileSize

								End If

							End Forall

						End If

					End Forall

				End If

			End If

		End If

	End If

	Set doc = dc.GetNextDocument(doc)

Wend 

Dim statement As String

statement = Cstr(counter) & " files located.  Total size - " & Cstr(fSize)

End Sub

Subject: Unable to access Items array in NotesDocument

Maybe the debugger just doesn’t want to show you what’s in notesdocument.items. It isn’t the world’s greatest IDE, it does stuff like that sometimes.

I don’t see why you would use isarray to test if doc.items is an array. Notesdocument.items returns an array of notesitems, or so it says in the help file.

I’m not sure why you would use a Forall to go through the array either. I know the help example does this, but it seems wasteful. Forall creates an implicitly defined Variant, but why would you do that? You know the datatype you want: notesitem. Use it. Go through the array from 0 to its ubound, assigning a notesitem reference to each element along the way:

dim item as notesitem

dim i as integer

for i = 0 to ubound(doc.items)

set item = doc.items(i)

next i

I think you are getting type mismatches in part becuase your script is not type-safe. Tighten it up a bit and I think those problems will go away. Use variants only where they are actually needed.

You are also mixing declarations with assignments. Stop that! Declarations first, in decreasing order of size, then assignments:

Dim session As NotesSession

Dim db As NotesDatabase

Dim dc As NotesDocumentCollection

set session = new notessession

Set db = session.GetDatabase(“xxxx”, False)

Set dc = db.AllDocuments

Then you are creating strings, and then converting them to dates on the fly. Why? If you need a date, create a date. There are date variants - common but a little messy - and notesdatetime objects. Notesdocument.created is a variant, so maybe variants would be best:

Dim crdate As String

Dim nowdate As String

Dim age As Integer

age = Now - doc.Created

I don’t know exactly what you will get when you subtract one date variant from another - is it actually a number of years? - but that looks like it will work. I guess if you are implicitly casting the result to an integer then time-differene-expressed-in-years is the likely outcome.

Subject: RE: Unable to access Items array in NotesDocument

"You are also mixing declarations with assignments. Stop that! Declarations first, in decreasing order of size, then assignments:

Dim session As NotesSession

Dim db As NotesDatabase

Dim dc As NotesDocumentCollection

set session = new notessession

Set db = session.GetDatabase(“xxxx”, False)

Set dc = db.AllDocuments"

I had that discussion before with Willy Lorenzo, and I still whole-heartedly disagree. Unless you can proof your point with more convincing arguments.

And while your at it, I’d like to see a reference to how Notes object variables differ in size, the compiler has to reserve for them.

Subject: RE: Unable to access Items array in NotesDocument

It makes a lot of sense in close-to-the-metal environments, particularly those that allow you to use direct memory address access to data to twiddle bits. You identify and segregate the “data” area of your memory space and it becomes easier to use addresses and offsets to find things. Big-to-small goes WAY back to calculating the amount of your enormous 4K memory workspace you’re going to lose in the process.

My personal preference is to get the Dims out of the way (and use variable names that avoid type ambiguity) in order to make the “active” code easier to read. Or, rather, that I can fit more of it on the screen at one go. Declarations are nice and all, but they do take up screen real estate. Interleaved declarations are a lot like double-spacing lines of code in that respect.

That doesn’t mean that I Dim everything at the top, though – even though we’ve found a use for more than 640KB, there’s not a lot of sense reserving memory space you’re not going to use, so I’ll defer declarations until they’re needed (while still segregating them from the stuff that does stuff). It’s just a stylistic preference, though, and I’d be likely to use this construct were it available:

Dim loopCounter As Integer = 0

which is more-or-less the way Java (or any C-syntax-derived language) would do it (and how VB.NET does it).

Subject: RE: Unable to access Items array in NotesDocument

Thanks for the responses, Stan and Harkpabst. This is more or less what I thought on the subject, but I am admittedly fairly new to Lotus development (and other development, for that matter).

Do you have any ideas regarding the problem I’m having with the document items array? I’m still stumped on this one, and any help would be appreciated.

Subject: RE: Unable to access Items array in NotesDocument

Stan, thanks a lot for your thoughts.

Declaring all you variables on top has been a technical requirement in most (probably all) early Basic implementations and personally I’m just suspicious, that this still is the main reason for many LotusScript developers to do so. I can (and will) not argue against personal taste. If you prefer to get declarations out of the way, that’s definitely one way to look at it and a valid argument. If on the other hand saving space on screen for the “real code” is not my primary concern, that’s my personal taste. Stuff that goes on top usually includes the typical base class objects like NotesSession, NotesUIWorkspace or my own typically reused classes. I don’t see that I loose anything by mixing declarations and initializations.

And of course I definitely agree to delaying declarations, if the flow of control is such that they might not be always needed. Still in these cases I tend to not keep my declarations in a separate block, but again I would never claim this to be anything but personal preference. A preference that is heavily influenced by languages that allow to have declaration and initialization on one line. I’d promote it more aggressively, if a statement like in your example was possible in LS.

It is my believe, that stuff like keeping your code well structured, using self-explanatory variable names, using appropriate levels of abstraction and so on does a lot more for code readability than keeping declarations out of the way. It’s just if someone strictly (and authoritatively) advises others to adopt his own style of coding without giving further explanations, I have to ask for the reasons. Now, it could be, that Thomas has been working on the LotusScript compiler and knows for sure, that what he suggested does benefit efficiency and performance. I can’t tell. But if so, I’d be grateful for a full explanation.

Same goes for the declaration in big-to-small order. I don’t say anything against it per-se. But there are those two things I’m struggling with:

What is the “size” of Domino objects?

Does it really matter to the compiler at the module level?

In languages like e.g. Java all reference variables have the same memory footprint (for the variable itself), so is LS really different here? I would not reject this recommendation if the difference is just negligible. If there is a “right” way to do it, I’d always opt for it, even if it doesn’t make a difference in most cases. But is there a difference at all? There’s only one reference to the order in which you should declare variables in the LotusScript documentation, and that’s about how to build user defined data types. If there’s more to it, I’m happy to learn that.

Again, thanks for throwing in your well respected opinion.

Subject: RE: Unable to access Items array in NotesDocument

Sure. It’s because it makes the script easier to read. If I’m looking for an Integer variable I know I will find it toward the bottom of the list, I don’t have to scan the whole thing. All declarations go at the top, for the same reason. Fewer bugs, too, if all assignments begin at a certain point, and not before.

Subject: RE: Unable to access Items array in NotesDocument

I see. No new arguments. Just an old Basic habit that became personal coding style.

Subject: RE: Unable to access Items array in NotesDocument

I originally tried to use the ubound statement to step through the items array, but that also throws a type mismatch. Even if I simply try to set an integer variable = ubound(doc.items), it throws this error, which is what originally made me think that the problem is related to not being able to access the Items array. By using the conditional IsArray, the script bypasses documents where the array cannot be accessed, which prevents the type mismatch error.

As for the other issues, I wasn’t aware that mixing declarations and assignments could cause problems - it just makes the script “flow” better for me when I have to revisit it.

Subject: Unable to access Items array in NotesDocument

Is it possible that you have a document in the database that has no items? You are grabbing every document in the database using the db.AllDocuments–if your database has multiple forms, it could be conceivable that one of the docs could have 0 items, so it would not have an items array. I remember back in the 4.6 days there was no such thing as a page, so if you wanted to do something like that, you had to create a form that may or may not have had items. If this database has that sort of legacy, I could see that being an issue.

just a thought.

brandt

Subject: RE: Unable to access Items array in NotesDocument

Thanks for the response, Brandt.

I would assume that it is possible for a document to have no items, but I also wrote a separate script to locate a particular document that I know should have items. I had found a document in a view and looked at the document properties to get a field value that I could focus on. I then directed the script to search for the document that had that field value. When the script found the document, it still could not access the Items array.

Since I could see fields listed in the document properties, my assumption was that the document would have items.

Subject: Problem solved

This ended up being a problem with the database. Our system administrator got the database restored (and no, I don’t know exactly what she did), and the script is working again.

Thanks to all of you that responded.

Subject: An actual solution (or workaround)

I just had the same problem and it may have been a corrupt document in the database, even though I’m not sure. However, the problem seems to appear as soon as you try to read the value from doc.Items. What you can do is to check the array for content with the IsEmpty function like this.

If Not(Isempty(doc.Items)) Then

Forall i In doc.Items

'do whatever

End Forall

End If

Works like a charm…

Subject: Another workaround, and, observations

I had the same problem.

I copied the document to a tiny database which had no known problems. All symptoms remained the same in the copy of the document. So, I doubt the DB itself was bad.

After I opened the document in the Notes Client (R7.0.3) and made a trivial change and saved it, the problem went away.

The origin of the document was that it was an email bounceback from another company. It passed through our new MS-Exchange hosting provider USA-Net, then Notes SMTP task and mail routing, on its way to the mail-in database. This never happened with previous hosts or when we had a pure Notes email system.

Is it another case of “DOS isn’t done until Lotus won’t run” ?