home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 3_2004-2005.ISO / Data / Zips / Sort_witho1782158152004.psc / cSort.cls < prev    next >
Text File  |  2004-08-15  |  16KB  |  492 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "cSort"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15.  
  16. 'Updates
  17. ''''''''
  18. '
  19. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  20. '15Aug2004      UMG
  21. '
  22. 'Changed all Asc to AscB
  23. 'Changed all Params to ByVal
  24. 'This gave an overall timing improvement by about 10 percent (tnx to ...)
  25. '
  26. 'Modified Property Let Alphabet and Property Let KeyTranslation
  27. 'Got rid of some superfluous variables and moved others into the appropriate modules
  28. 'Added TranslateKey by Array rather then by ASC(Mid$...
  29. '
  30. 'Did a little code cosmetic and added notes and comments
  31. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  32.  
  33. 'Enumerations
  34. Public Enum SortDirection
  35.     Ascending = 1
  36.     Descending = -1
  37. End Enum
  38.  
  39. Public Enum PartialKeys
  40.     LessFullKeys = 1
  41.     GreaterFullKeys = 2
  42.     NotAllowed = 3
  43. End Enum
  44.  
  45. #If False Then
  46. 'Preserve Capitalization
  47. Private Ascending, Descending, LessFullKeys, GreaterFullKeys, NotAllowed
  48. #End If
  49.  
  50. 'Event Declarations
  51. Public Event QueryKey(ByVal SortId As Long, ByVal Pointer As Long, ByRef Key As String)
  52. Public Event NextPointer(ByVal SortId As Long, ByVal Pointer As Long, ByRef Cancel As Boolean)
  53. Attribute NextPointer.VB_MemberFlags = "200"
  54.  
  55. 'Property Variables
  56. Private myLowBound          As Long
  57. Private myHighBound         As Long
  58. Private myKeySize           As Long 'the actual keysize is one more
  59. Private myKeyPosition       As Long
  60. Private mySortId            As Long
  61. Private myAlphabet          As String
  62. Private myKeyTranslation    As String
  63. Private myRightToLeft       As Boolean
  64. Private mySortDirection     As SortDirection
  65. Private myPartialKeys       As PartialKeys
  66.  
  67. 'Working Variables
  68. Private Anchor()            As Long
  69. Private Chain()             As Long
  70. Private TranslateKey(1 To 256)      As Long
  71. Private KeyAscB             As Long
  72. Private Idx                 As Long
  73. Private CntUniq             As Long
  74. Private Busy                As Boolean
  75. Private Cancel              As Boolean
  76. Private XlatKey             As Boolean
  77. Private Indirect            As Boolean
  78. Private QueriedKey          As String
  79. Private DummyTable()        As String
  80.  
  81. Public Property Get Alphabet() As String
  82.  
  83.     Alphabet = myAlphabet
  84.  
  85. End Property
  86.  
  87. Public Property Let Alphabet(ByVal nuAlphabet As String)
  88.  
  89.   Dim Pos       As Long
  90.   Dim Char      As Long
  91.   Dim HiChar    As String
  92.  
  93.     CheckBusy
  94.     HiChar = Chr$(255)
  95.     XlatKey = False
  96.     myAlphabet = vbNullString
  97.     myKeyTranslation = vbNullString
  98.     Select Case Len(nuAlphabet)
  99.       Case Is > 256
  100.         ShowError 380, "Sort", "Alphabet is too long."
  101.       Case Is > 0
  102.         myAlphabet = nuAlphabet
  103.         myKeyTranslation = String$(256, HiChar)
  104.         For Pos = 1 To Len(myAlphabet)
  105.             If InStr(Pos + 1, myAlphabet, Mid$(myAlphabet, Pos, 1)) Then
  106.                 ShowError 380, "Sort", "Alphabet character '" + Mid$(myAlphabet, Pos, 1) & "' is not unique."
  107.               Else 'NOT INSTR(Pos...
  108.                 Mid$(myKeyTranslation, AscB(Mid$(myAlphabet, Pos, 1)) + 1, 1) = Chr$(Char)
  109.                 Char = Char + 1
  110.             End If
  111.         Next Pos
  112.         Pos = 0
  113.         Do Until Char > AscB(HiChar)
  114.             Pos = Pos + 1
  115.             If Mid$(myKeyTranslation, Pos, 1) = HiChar Then 'not yet replaced
  116.                 Mid$(myKeyTranslation, Pos, 1) = Chr$(Char)
  117.                 Char = Char + 1
  118.             End If
  119.         Loop
  120.         XlatKey = True
  121.     End Select
  122.  
  123. End Property
  124.  
  125. Private Sub BuildAndOutputChains(Table() As String, ByVal Level As Long, ByVal Start As Long)
  126.  
  127.   Dim Ptr       As Long 'recursive variables, have to be here
  128.   Dim NextPtr   As Long
  129.   Dim LoKey     As Long
  130.   Dim HiKey     As Long
  131.  
  132.     If myRightToLeft Then
  133.         Idx = myKeyPosition - Level
  134.       Else 'MYRIGHTTOLEFT = FALSE/0
  135.         Idx = myKeyPosition + Level
  136.     End If
  137.     LoKey = 257
  138.     HiKey = 0
  139.     Ptr = Start 'initial pointer into chain
  140.  
  141.     'cut and re-link chain(s)
  142.     Do
  143.         'get key value for this level
  144.         KeyAscB = 0 'reset first
  145.         If Indirect Then 'get key from client
  146.             RaiseEvent QueryKey(mySortId, Ptr, QueriedKey)
  147.             If Idx >= 1 And Idx <= Len(QueriedKey) Then
  148.                 KeyAscB = AscB(Mid$(QueriedKey, Idx, 1)) + 1
  149.             End If
  150.           Else 'take key from table 'INDIRECT = FALSE/0
  151.             If Idx >= 1 And Idx <= Len(Table(Ptr)) Then
  152.                 KeyAscB = AscB(Mid$(Table(Ptr), Idx, 1)) + 1
  153.             End If
  154.         End If
  155.         If KeyAscB Then 'we have a key value (ie the key is long enough to be examined at this level)
  156.             If XlatKey Then 'need translation
  157.                 KeyAscB = TranslateKey(KeyAscB)
  158.             End If
  159.           Else 'key is too short 'KEYASCB = FALSE/0
  160.             Select Case myPartialKeys
  161.               Case GreaterFullKeys
  162.                 KeyAscB = 257
  163.               Case NotAllowed
  164.                 ShowError 5, "Sort", "Incomplete key in element(" & Ptr & ")."
  165.             End Select
  166.         End If
  167.  
  168.         'save range of keys for this level
  169.         If KeyAscB < LoKey Then
  170.             LoKey = KeyAscB
  171.         End If
  172.         If KeyAscB > HiKey Then
  173.             HiKey = KeyAscB
  174.         End If
  175.  
  176.         'extend chain on this anchor, the anchor being selected by the recursion level and
  177.         'the sort key byte value of the current element in the table at this level (which
  178.         'in turn corresponds to the byte position within the key)
  179.  
  180.         NextPtr = Chain(Ptr) 'save pointer to next chain member temporarily
  181.  
  182.         'put current anchor value in chain - this is either zero when this anchor did not yet
  183.         'point to a chain (this zero now indicating end of chain), or it is the pointer to
  184.         'the previous start of a chain, this pointer now becomes a member of the chain
  185.         Chain(Ptr) = Anchor(Level, KeyAscB)
  186.  
  187.         'put current pointer into anchor as new pointer to the start of a chain
  188.         Anchor(Level, KeyAscB) = Ptr
  189.  
  190.         Ptr = NextPtr 'continue with next chain member (pointed to by this chain member)...
  191.     Loop While Ptr '...if any
  192.  
  193.     If mySortDirection = Descending Then
  194.         'exchange LoKey and HiKey because we will scan the chains in the opposite direction
  195.         LoKey = LoKey Xor HiKey
  196.         HiKey = HiKey Xor LoKey
  197.         LoKey = LoKey Xor HiKey
  198.     End If
  199.  
  200.     'now that the chains have been re-linked let's have a look at each one of them
  201.     '
  202.     'a  - if there are any chains with one member only then output that member; it has a
  203.     '     unique sort key.
  204.     '
  205.     'b  - chains with more than one member require further examination:
  206.     '
  207.     'b1 - if we are at the end of the key we can safely assume that nothing more will
  208.     '     happen so output all members of those chains; they all have an identical sort key.
  209.     '
  210.     'b2 - if we are not at the end of the key then chains with more than one member
  211.     '     may need to be further subdivided by recursion; call myself pointing to the
  212.     '     start of the chains in question and advance to the next byte in the sort key.
  213.     '
  214.     For NextPtr = LoKey To HiKey Step mySortDirection 'scan chains in saved key range
  215.         Ptr = Anchor(Level, NextPtr)
  216.         If Ptr Then 'the anchor points to a chain start
  217.             Anchor(Level, NextPtr) = 0 'clear this anchor (it may possibly come up again)
  218.             If Chain(Ptr) Then 'there is a chain with at least two members on this anchor
  219.                 If Level = myKeySize Then 'all members of the chain have identical keys
  220.                     Do 'so output them one after t'other
  221.                         RaiseEvent NextPointer(mySortId, Ptr, Cancel)
  222.                         Ptr = Chain(Ptr) 'follow chain...
  223.                     Loop While Ptr And Cancel = False '...to it's end
  224.                     CntUniq = CntUniq + 1
  225.                   Else 'keys of chain members may still be different, recursion to next level 'NOT LEVEL...
  226.                     BuildAndOutputChains Table(), Level + 1, Ptr
  227.                 End If
  228.               Else 'chain start is chain end, so one member only, key is unique 'CHAIN(PTR) = FALSE/0
  229.                 RaiseEvent NextPointer(mySortId, Ptr, Cancel) 'so out with it
  230.                 CntUniq = CntUniq + 1
  231.             End If
  232.         End If
  233.         If Cancel Then
  234.             Exit For 'loopávarying nextptr
  235.         End If
  236.     Next NextPtr
  237.  
  238. End Sub
  239.  
  240. Private Sub CheckBusy()
  241.  
  242.     If Busy Then
  243.         ShowError 5, "Sort", "You cannot alter properties or perform a sort while a sort is busy."
  244.     End If
  245.  
  246. End Sub
  247.  
  248. Private Sub Class_Initialize()
  249.  
  250.   'set defaults
  251.  
  252.     myKeySize = 0 'the actual keysize is one more
  253.     myKeyPosition = 1
  254.     mySortDirection = Ascending
  255.     myPartialKeys = NotAllowed
  256.     myRightToLeft = False
  257.  
  258. End Sub
  259.  
  260. Public Property Get HighBound() As Long
  261.  
  262.     HighBound = myHighBound
  263.  
  264. End Property
  265.  
  266. Public Property Let HighBound(ByVal nuHighBound As Long)
  267.  
  268.     CheckBusy
  269.     myHighBound = nuHighBound
  270.  
  271. End Property
  272.  
  273. Public Property Get KeyPosition() As Long
  274.  
  275.     KeyPosition = myKeyPosition
  276.  
  277. End Property
  278.  
  279. Public Property Let KeyPosition(ByVal nuKeyPosition As Long)
  280.  
  281.     CheckBusy
  282.     If nuKeyPosition < 1 Or nuKeyPosition > 65535 Then
  283.         ShowError 380, "Sort", "KeyPosition must be below 64k."
  284.       Else 'NOT NUKEYPOSITION...
  285.         myKeyPosition = nuKeyPosition
  286.     End If
  287.  
  288. End Property
  289.  
  290. Public Property Get KeySize() As Long
  291.  
  292.     KeySize = myKeySize + 1
  293.  
  294. End Property
  295.  
  296. Public Property Let KeySize(ByVal nuKeySize As Long)
  297.  
  298.     CheckBusy
  299.     If nuKeySize < 1 Or nuKeySize > 256 Then
  300.         ShowError 380, "Sort", "KeySize must be from 1 thru 256."
  301.       Else 'NOT NUKEYSIZE...
  302.         myKeySize = nuKeySize - 1
  303.     End If
  304.  
  305. End Property
  306.  
  307. Public Property Get KeyTranslation() As String
  308.  
  309.     KeyTranslation = myKeyTranslation
  310.  
  311. End Property
  312.  
  313. Public Property Let KeyTranslation(ByVal nuKeyTranslation As String)
  314.  
  315.     CheckBusy
  316.     XlatKey = False
  317.     myKeyTranslation = vbNullString
  318.     Select Case Len(nuKeyTranslation)
  319.       Case 256
  320.         myKeyTranslation = nuKeyTranslation
  321.         XlatKey = True
  322.       Case 0
  323.         'do nothing
  324.       Case Else
  325.         ShowError 380, "Sort", "KeyTranslation must be 256 characters long."
  326.     End Select
  327.  
  328. End Property
  329.  
  330. Public Property Get LowBound() As Long
  331.  
  332.     LowBound = myLowBound
  333.  
  334. End Property
  335.  
  336. Public Property Let LowBound(ByVal nuLowBound As Long)
  337.  
  338.     CheckBusy
  339.     myLowBound = nuLowBound
  340.  
  341. End Property
  342.  
  343. Public Property Get PartialKeys() As PartialKeys
  344.  
  345.     PartialKeys = myPartialKeys
  346.  
  347. End Property
  348.  
  349. Public Property Let PartialKeys(ByVal nuPartialKeys As PartialKeys)
  350.  
  351.     CheckBusy
  352.     If nuPartialKeys <> LessFullKeys And nuPartialKeys <> GreaterFullKeys And nuPartialKeys <> NotAllowed Then
  353.         ShowError 380, "Sort", "Value for PartialKeys is incorrect."
  354.       Else 'NOT NUPARTIALKEYS...
  355.         myPartialKeys = nuPartialKeys
  356.     End If
  357.  
  358. End Property
  359.  
  360. Public Property Get RightToLeft() As Boolean
  361.  
  362.     RightToLeft = myRightToLeft
  363.  
  364. End Property
  365.  
  366. Public Property Let RightToLeft(ByVal nuRightToLeft As Boolean)
  367.  
  368.     CheckBusy
  369.     myRightToLeft = CBool(nuRightToLeft)
  370.  
  371. End Property
  372.  
  373. Private Sub ShowError(Number As Long, Source As String, Optional Description As String)
  374.  
  375.   Dim MP As Long
  376.  
  377.     MP = Screen.MousePointer
  378.     Screen.MousePointer = vbDefault
  379.     If Len(Description) Then
  380.         Err.Raise Number, Source, Description
  381.       Else 'LEN(DESCRIPTION) = FALSE/0
  382.         Err.Raise Number, Source
  383.     End If
  384.     Screen.MousePointer = MP
  385.  
  386. End Sub
  387.  
  388. Public Property Get SortDirection() As SortDirection
  389.  
  390.     SortDirection = mySortDirection
  391.  
  392. End Property
  393.  
  394. Public Property Let SortDirection(ByVal nuSortDirection As SortDirection)
  395.  
  396.     CheckBusy
  397.     If nuSortDirection <> Ascending And nuSortDirection <> Descending Then
  398.         ShowError 380, "Sort", "Value for sort direction is incorrect."
  399.       Else 'NOT NUSORTDIRECTION...
  400.         mySortDirection = nuSortDirection
  401.     End If
  402.  
  403. End Property
  404.  
  405. Public Function SortIndirect(Optional ByVal SortId As Long = 0) As Long
  406.  
  407.     Indirect = True
  408.     SortIndirect = SortIt(DummyTable(), SortId)
  409.  
  410. End Function
  411.  
  412. Private Function SortIt(Table() As String, ByVal SortId As Long) As Long
  413.  
  414.   Dim Reverse   As Boolean
  415.  
  416.     CheckBusy
  417.     If myLowBound <= myHighBound And Sgn(myLowBound) = Sgn(myHighBound) Then
  418.  
  419.         'prepare
  420.         Busy = True
  421.         Cancel = False
  422.         CntUniq = 0
  423.         mySortId = SortId
  424.         If XlatKey Then
  425.             'translation array
  426.             For Idx = 1 To 256
  427.                 TranslateKey(Idx) = AscB(Mid$(myKeyTranslation, Idx, 1))
  428.             Next Idx
  429.         End If
  430.  
  431.         'anchor array
  432.         ReDim Anchor(0 To myKeySize, 0 To 257) 'myKeySize is one less than the key length
  433.  
  434.         'create initial chain
  435.  
  436.         'there is a positional(!) relationship between the chain members and
  437.         'the elements in the table, however the chain is a linked list with each
  438.         'member pointing to the next in sequence, so therefore we can (later on)
  439.         'indirectly re-arrange the sequence of the elements in the table, by cutting
  440.         'and re-linking the chain(s).
  441.         '
  442.         'the last member in a chain of course does not point to the next and therefore
  443.         'it has a zero indicating end of chain; though the positional relationship
  444.         'to the corresponding element in the table still exists.
  445.         '
  446.         'the array of anchors has pointers that point to the beginning of each chain.
  447.  
  448.         If myKeySize And 1 Then 'odd keysize
  449.             Reverse = (mySortDirection = Ascending)
  450.           Else 'NOT MYKEYSIZE...
  451.             Reverse = (mySortDirection = Descending)
  452.         End If
  453.         If Reverse Then 'initial links must point downward in order to make the sort stable
  454.             ReDim Chain(myLowBound To myHighBound + 1)
  455.             'build chain
  456.             For Idx = myLowBound To myHighBound
  457.                 Chain(Idx + 1) = Idx
  458.             Next Idx
  459.             Idx = myHighBound 'set to start of chain
  460.           Else 'initial links must point upward in order to make the sort stable 'REVERSE = FALSE/0
  461.             ReDim Chain(myLowBound - 1 To myHighBound)
  462.             'build chain
  463.             For Idx = myLowBound To myHighBound
  464.                 Chain(Idx - 1) = Idx
  465.             Next Idx
  466.             Idx = myLowBound 'set to start of chain
  467.         End If
  468.  
  469.         BuildAndOutputChains Table(), 0, Idx 'this is the sort proper
  470.  
  471.         SortIt = CntUniq
  472.         Erase Anchor, Chain 'release memory
  473.         Busy = False
  474.       Else 'NOT MYLOWBOUND...
  475.         ShowError 17, "Sort", "Illegal sort bounds (" & Format$(myLowBound) & " To " & Format$(myHighBound) & ")."
  476.     End If
  477.  
  478. End Function
  479.  
  480. Public Function SortTable(Table() As String, Optional ByVal SortId As Long = 0) As Long
  481.  
  482.     Indirect = False
  483.     If myLowBound >= LBound(Table) And myHighBound <= UBound(Table) Then
  484.         SortTable = SortIt(Table(), SortId)
  485.       Else 'NOT MYLOWBOUND...
  486.         ShowError 9, "Sort", "Sort bounds (" & myLowBound & " To " & myHighBound & ") outside table bounds."
  487.     End If
  488.  
  489. End Function
  490.  
  491. ':) Ulli's VB Code Formatter V2.17.4 (2004-Aug-15 13:25) 65 + 412 = 477 Lines
  492.