How to use the Grove OLE Automation Class in Visual Basic 5.0

Registering DLLs

To get started you must copy all the dll files distributed with Jade except for groveoa.dll into the NT Windows\System32 or 95 Windows\System directory. Copy groveoa.dll into the directory named \Program Files\Common Files\OleSvr on the same drive that Windows was installed on (if the OleSvr directory doesn't exist then create it). While in that directory type regsrv32 groveoa.dll. Alternately download the sample program and the setup installation program will do all of the above for you.
 

What it is good at

The Grove OLE Automation Class is basically intended for parsing and fully supports the 9401 catalog.
It is extremely fast and easy to use.
 

What it is not so good at

The majority of the properties and methods needed for SPAM type functionality are not available in the grove.
The Grove API does not allow access to the doctype declaration or document type declaration subset.
Unconditionally substitutes general entities with a space unless explicit numeric character references are made (with the exception of &amp; and &lt; which are unconditionally substituted to & and < respectively).

The sample source code touches on normalization and does output the doctype declaration (when in the document instance) and document type declaration subset. There are a few bugs in the normalization that you will have to Band-Aid if you intend to use it (lost data in buffer). If you really want to normalize you can try my SP OLE Automation  wrapper around SPAM which comes with SP Wizard.

Classes not availble, but show in the object browser

This information is based on my experimentation and may not be completely accurate. A red line indicates that the class is not accessible.

Here are the basic steps involved in using the grove to parse documents:

Create an instance.
Set the catalog paths and input document file name.
Set Warning toggles.
Call the parser to parse the instance.
Display error messages.

Partial code to Create a Parser:

Note this code is only partial, download the sample program if you want to use the code in VB5. The sample program uses EMEDIT32.OCX the Early Morning Editor Version 2.0, you will have to purchase this OCX if you want to get rid of the nag screen.
Create a module and place the following code in the General Declarations section.

    Option Explicit
 
    '**************************************
    '*  Debug
    '**************************************
    #Const MODULE_SPOLEA_DEBUG = "YES"
'    #Const MODULE_SPOLEA_DEBUG = "NO"
 
    '**************************************
    '*  Prevent 16 bit compile
    '**************************************
    #If Win16 Then
    You Can 't compile this application for 16 bit platform
    #End If
 
    '**************************************
    '*  Grove
    '**************************************
 
    '------------------------
    'Classes/Modules (cm prefix)
 Public cmGroveBuilder As New GroveBuilder
    Public cmExternalID As ExternalIdNode
    Public cmMessageNode As MessageNode
    Public cmSgmlDocumentNode As SgmlDocumentNode
    Public cmStoragePos As StoragePos
    'Methods/Properties (mp prefix)
    Public mpDocumentElement As ElementNode
    Public mpFirstChild As Node
    Public mpFirst As Node
    Public mpNextSibling As Node
 
    '------------------------
    'Constants
 
    Public Const ENTITY_TYPE_CDATA = 1
    Public Const ENTITY_TYPE_NDATA = 3
    Public Const ENTITY_TYPE_PI = 5
    Public Const ENTITY_TYPE_SDATA = 2
    Public Const ENTITY_TYPE_SUBDOCUMENT = 4
    Public Const ENTITY_TYPE_TEXT = 0
 
    Public Const ERROR_TYPE_AFDR = 2
    Public Const ERROR_TYPE_IDREF = 0
    Public Const ERROR_TYPE_LPDNOTATION = 3
    Public Const ERROR_TYPE_SIGNIFICANT = 1
    Public Const ERROR_TYPE_VALID = 4
 
    Public Const SEVERITY_ERROR = 2
    Public Const SEVERITY_INFO = 0
    Public Const SEVERITY_WARNING = 1
 
    Public Const WARNING_SGMLDECL = 0
    Public Const WARNING_DUPLICATE_ENTITY = 1
    Public Const WARNING_SHOULD = 2
    Public Const WARNING_UNDEFINED_ELEMENT = 3
    Public Const WARNING_DEFAULT_ENTITY_REFERENCE = 4
    Public Const WARNING_MIXED_CONTENT = 5
    Public Const WARNING_UNCLOSED_TAG = 6
    Public Const WARNING_NET = 7
    Public Const WARNING_EMPTY_TAG = 8
    Public Const WARNING_UNUSED_MAP = 9
    Public Const WARNING_UNUSED_PARAM = 10
    Public Const WARNING_NOTATION_SYSTEM_ID = 11
 
    '**************************************
    '*  Windows API
    '**************************************
    Private Const WM_USER = &H400
    Private Const LB_SETHORIZONTALEXTENT = &H194
    Private HighTextSize As Long
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, _
        ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
    Public Declare Function WritePrivateProfileString Lib "kernel32" _
        Alias "WritePrivateProfileStringA" (ByVal AppName As String, _
        ByVal KeyName As String, ByVal keydefault As String, _
        ByVal FileName As String) As Long
    Public Declare Function GetPrivateProfileString Lib "kernel32" _
        Alias "GetPrivateProfileStringA" (ByVal AppName As String, _
        ByVal KeyName As String, ByVal keydefault As String, _
        ByVal ReturnString As String, ByVal NumBytes As Long, _
        ByVal FileName As String) As Long
 

    '---------------------------
    'Error Constants and variables
    '---------------------------
    Public DriveError As Integer
    Public Const DEVICE_NOT_AVAILABLE = 4
    Public Const MOUSE_POINTER_HOURGLASS = 11
    Public Const MOUSE_POINTER_NORMAL = 0

    '---------------------------
    'Misc Objects
    '---------------------------
    'Public objErrorFile As New TextFile
   ' Public objPassedParse As New TextFile
 
    'Public iFileIndex As Integer

    'Public sPassword As String
 

    Public GID As String
 

Create a command button on the main form named cmdbuttonParse and place the following code in its click event:
cmdbuttonParseClick
Add the following code to the General Declarations section of the module you previously created.

Public Sub cmdbuttonParseClick()
   'Trap all Errors local to this Sub.
    On Error GoTo cmdbuttonParseClickErr
 
    'Local variables.
    Dim lSiblingIndex As Long
    Dim lWidthOfText As Long
    Dim iFileIndex As Integer
    Dim lEventsAllowed As Long
    Dim sMessageLine As String
    Dim Ret As Variant
 
    formViewFile.Hide
    If formSPOLEA.listboxPassed.ListCount Then
        Ret = MsgBox(" Do you want to clear the list of files that passed the parse from the previous press of the parse button?", 36, "Clear")
        If Ret = 6 Then
            formSPOLEA.listboxPassed.Clear
        End If
    End If
    formSPOLEA.MousePointer = MOUSE_POINTER_HOURGLASS
    'Disable command buttons so the user doesn't prees them twice.
    formSPOLEA.cmdbuttonParse.Enabled = False
    formSPOLEA.cmdButtonNormalize.Enabled = False
 
    'Clear out anything that may be loaded in the listbox
    'from a previous press of the Parse button.
    formSPOLEA.listboxResults.Clear
    ClearHorizontalScrollBars formSPOLEA.listboxResults
 
    'Clear out the error message text box.
    formSPOLEA.tbErrMessage.Text = ""
 
    'Begin Processing all file(s) selected
    For iFileIndex = 0 To formSPOLEA.File1.ListCount - 1
        lEventsAllowed = DoEvents()
        If formSPOLEA.File1.Selected(iFileIndex) = True Then
            If Dir$(formSPOLEA.File1.List(iFileIndex)) <> "" Then
                SetSPToggles
                Set cmSgmlDocumentNode = cmGroveBuilder.parse(formSPOLEA.File1.List(iFileIndex))
            Else
                MsgBox "File " & formSPOLEA.File1.List(iFileIndex) & " not found in current directory."
                Exit Sub
            End If
            '****************************
            'Parse and display error messages
            '****************************
            DisplayMessages cmSgmlDocumentNode, iFileIndex
        End If
    Next iFileIndex
 
    'Turn on horizontal scroll bars for the list box.
    lWidthOfText = WidestEntryWidth(formSPOLEA.listboxResults) * 0.1
    HorizontalScrollBars formSPOLEA.listboxResults, lWidthOfText
    formSPOLEA.listboxPassed.Refresh
    lWidthOfText = WidestEntryWidth(formSPOLEA.listboxPassed) * 0.1
    HorizontalScrollBars formSPOLEA.listboxPassed, lWidthOfText
 
    'Enable command buttons.
    formSPOLEA.cmdbuttonParse.Enabled = True
    formSPOLEA.cmdButtonNormalize.Enabled = True
    formSPOLEA.MousePointer = MOUSE_POINTER_NORMAL
    Exit Sub

'Error Handler
cmdbuttonParseClickErr:
    formSPOLEA.MousePointer = MOUSE_POINTER_NORMAL
    'Enable command buttons.
    formSPOLEA.cmdbuttonParse.Enabled = True
    formSPOLEA.cmdButtonNormalize.Enabled = True
 
    'Display error to user
    MsgBox "Error: " + Date$ + " " + Time$ + " in moduleSPOLEA/cmdbuttonParseClick Sub, " + Error(Err)
 
    'Turn on horizontal scroll bars for the list boxes
    'just in case the parser returned something.
    lWidthOfText = WidestEntryWidth(formSPOLEA.listboxResults) * 0.1
    HorizontalScrollBars formSPOLEA.listboxResults, lWidthOfText
    lWidthOfText = WidestEntryWidth(formSPOLEA.listboxPassed) * 0.1
    HorizontalScrollBars formSPOLEA.listboxPassed, lWidthOfText
    #If MODULE_SPOLEA_DEBUG = "YES" Then
        Dim DB
        DB = FreeFile
        'Write errors to a file if debug mode.
        Open App.Path + "\debug.txt" For Append As #DB
            Print #DB, "Error: " + Date$ + " " + Time$ + " in moduleSPOLEA/cmdbuttonParseClick Sub, " + Error(Err)
        Close #DB
    #End If
    Exit Sub
End Sub
 
 
 

Public Sub DisplayMessages(cmSgmlDocumentNode As SgmlDocumentNode, iFileIndex As Integer)
    'Trap all Errors local to this Sub.
    On Error GoTo DisplayMessagesErr
 
    'Local variables.
    Dim lSiblingIndex As Long
    Dim sMessageLine As String
 
    'See if there were any error(s)/warning(s)
    If cmSgmlDocumentNode.Messages Is Nothing Then
        'Add to the passed parse list.
        'There were no error(s)/warning(s) so process next file.
        formSPOLEA.listboxPassed.AddItem formSPOLEA.Dir1.Path + "\" + formSPOLEA.File1.List(iFileIndex)
    Else
        'Yes there were error(s)/warning(s).
        'Iterate through messages
        Set cmMessageNode = cmSgmlDocumentNode.Messages.[First]
 cmGroveBuilder.DefaultCatalogs = ".\catalog;" + formEnvironment.Text1.Text
        cmGroveBuilder.DefaultDirectories = formEnvironment.Text2.Text
        formSPOLEA.Caption = "SP Parser/Normalizer (SGML) [" + formSPOLEA.File1.List(iFileIndex) + "]"
        Do While Not (cmMessageNode Is Nothing)
            Set cmStoragePos = cmMessageNode.StoragePos
            'Build a message line. Note that square brackets are used to avoid
            'conflicts between VB reserved words, when not sure use them anyway
            'see Microsoft Knowledge Base article Q115859.
            Select Case cmMessageNode.Severity
                Case Is = SEVERITY_INFO
                    sMessageLine = "Information for file::"
                Case Is = SEVERITY_WARNING
                    sMessageLine = "Warning in file::"
                Case Is = SEVERITY_ERROR
                    sMessageLine = "Error in file::"
            End Select
'            sMessageLine = sMessageLine + formSPOLEA.Dir1.Path + "\" + cmStoragePos.StorageObjectId + "::"
            sMessageLine = sMessageLine + cmStoragePos.StorageObjectId + "::"
'Problem 001:Q125749
            'Calling cmStoragePos.StorageManagerName results in a R6025 runtime,
            'error (pure virtual function call) see Microsoft Knowledge base
            'article Q125749.
'                    sMessageLine = sMessageLine + cmStoragePos.StorageManagerName + "::"
            sMessageLine = sMessageLine + "Message Text::" + cmMessageNode.[Text] + "::"
            sMessageLine = sMessageLine + "Line Number::" + Trim(Str(cmStoragePos.[LineNumber])) + "::"
            sMessageLine = sMessageLine + "ByteIndex::" + Trim(Str(cmStoragePos.[ByteIndex])) + "::"
            sMessageLine = sMessageLine + "Character Index::" + Trim(Str(cmStoragePos.[CharacterIndex])) + "::"
            sMessageLine = sMessageLine + "Column Number::" + Trim(Str(cmStoragePos.[ColumnNumber]))
            'Add the current error/warning message to the list box.
            formSPOLEA.listboxResults.AddItem sMessageLine
'Problem 002:
            'There is a problem with SiblingIndex. It always returns 0.
            lSiblingIndex = cmMessageNode.[SiblingIndex]
            'Go get the next error/warning message.
            Set cmMessageNode = cmMessageNode.[NextSibling]
        Loop
    End If
    Exit Sub

'Error Handler
DisplayMessagesErr:
 
    'Display error to user.
    MsgBox "Error: " + Date$ + " " + Time$ + " in moduleSPOLEA/DisplayMessagesErr Sub, " + Error(Err)
 
    'Debug
    #If MODULE_SPOLEA_DEBUG = "YES" Then
        Dim DB
        DB = FreeFile
        'Write errors to a file if debug mode.
        Open App.Path + "\debug.txt" For Append As #DB
            Print #DB, "Error: " + Date$ + " " + Time$ + " in moduleSPOLEA/DisplayMessagesErr Sub, " + Error(Err)
        Close #DB
    #End If
    Exit Sub
End Sub
 
Public Sub SetSPToggles()
    Dim i As Integer
    'Values are listed below for your information.
    'WARNING_SGMLDECL = 0
    'WARNING_DUPLICATE_ENTITY = 1
    'WARNING_SHOULD = 2
    'WARNING_UNDEFINED_ELEMENT = 3
    'WARNING_DEFAULT_ENTITY_REFERENCE = 4
    'WARNING_MIXED_CONTENT = 5
    'WARNING_UNCLOSED_TAG = 6
    'WARNING_NET = 7
    'WARNING_EMPTY_TAG = 8
    'WARNING_UNUSED_MAP = 9
    'WARNING_UNUSED_PARAM = 10
    'WARNING_NOTATION_SYSTEM_ID = 11
    For i = 0 To 11
        If formSPSettings.SSCheck1(i).Value Then
            cmGroveBuilder.Warning(i) = True
        Else
            cmGroveBuilder.Warning(i) = False
        End If
    Next i
End Sub
 
 

Disclaimer

Permission is hereby granted, free of charge, to any person obtaining a copy of SPOLEA and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use and to permit persons to whom the Software is furnished to do so., subject to the following conditions:

THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL LARRY ROBERTSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

PLEASE BACKUP ALL OF YOUR FILES BEFORE USING THIS PRODUCT!


The sample program includes:
Parse sgml and html files.The html dtds are included along with some html sample files in the data directory that have parsing errors.
It will print reports indicating the files that passed and did not pass the parse.
Multiple files may be selected.
Double click on the error message and the document is opened in an editor and the nearest word to the error is highlighted.
Built in debugging code that records system and error information.
A normalize function is also provided but it has some bugs. If you should get the bugs fixed let me know so I can fix them on my end as well.

NOTE: This sample program has a menu item named Security. When you click this item you will be prompted for a password, the password is the word "password". This was installed to prevent the average user from having access to the Setup menu item which allows you to set the warning toggles for the parser. You might want to remove the code that prompts for a password if it does not apply to your needs.

Download the latest SP Grove OLE Automation Sample with Source Code Now