<b>=============== Summary ===============</b><br>
<br>
Change Set:        normalize-paths<br>
Date:            9 July 2022<br>
Author:            Christoph Thiede<br>
<br>
Adds interface for path normalization on FileDirectory that removes path navigators such as <font color="#800080">'.'</font> or <font color="#800080">'..'</font>. Tests new <font color="#000080">#withNormalizedPath</font> for Windows and Unix file directories. Applies path normlization in file dialogs so that entering <font color="#800080">'../'</font> in the input field will trigger a correct selection of the parent node in the tree.<br>
<br>
<b>=============== Diff ===============</b><br>
<br>
<b>DosFileDirectory class>>forPathParts: {instance creation} · ct 7/9/2022 19:43</b><br>
<font color="#FF0000">+ forPathParts: pathParts<br>
+ <br>
+     | fileName partsStream |<br>
+     fileName := String streamContents: [:stream |<br>
+         partsStream := pathParts readStream.<br>
+         (self isDriveLetterRoot: partsStream peek)<br>
+             ifTrue: [stream nextPutAll: partsStream next]<br>
+             ifFalse: ["UNC path/network drive" stream nextPutAll: self slash].<br>
+         partsStream do: [:part |<br>
+             stream<br>
+                 nextPutAll: self slash;<br>
+                 nextPutAll: part]].<br>
+     ^ self on: fileName</font><br>
<br>
<b>DosFileDirectory class>>isDrive: {platform specific} · ct 7/9/2022 17:17 (changed)</b><br>
isDrive: fullName<br>
<s><font color="#0000FF">-     "Answer whether the given full name describes a 'drive', e.g., one of the root directories of a Win32 file system. We allow two forms here - the classic one where a drive is specified by a letter followed by a colon, e.g., 'C:', 'D:' etc. and the network share form starting with double-backslashes e.g., '\\server'."<br>
-     ^ (fullName size = 2 and: [fullName first isLetter and: [fullName last = $:]])<br>
-         or: [(fullName beginsWith: '\\') and: [(fullName occurrencesOf: $\) = 2]]<br>
</font></s><font color="#FF0000">+     "Answer whether the given full name describes a 'drive', e.g., one of the root directories of a Win32 file system. We allow two forms here - the classic one where a drive is specified by a letter followed by a colon, e.g., 'C:', 'D:' etc., and the UNC/network share form starting with double-backslashes e.g., '\\server' or '\\localhost\c$\Users'."<br>
+ <br>
+     ^ (self isDriveLetterRoot: fullName)<br>
+         or: [self isUNCRoot: fullName]</font><br>
<br>
<b>DosFileDirectory class>>isDriveLetterRoot: {platform specific} · ct 7/9/2022 17:16</b><br>
<font color="#FF0000">+ isDriveLetterRoot: fullName<br>
+     "e.g., 'C:', 'D:' etc."<br>
+ <br>
+     ^ fullName size = 2 and: [fullName first isLetter] and: [fullName second = $:]</font><br>
<br>
<b>DosFileDirectory class>>isUNCRoot: {platform specific} · ct 7/9/2022 17:17</b><br>
<font color="#FF0000">+ isUNCRoot: fullName<br>
+     "e.g., '\\server' or '\\localhost\c$\Users'"<br>
+ <br>
+     ^ (fullName beginsWith: '\\') and: [(fullName occurrencesOf: $\) = 2]</font><br>
<br>
<b>DosFileDirectory>>withNormalizedPath {path access} · ct 7/9/2022 19:55</b><br>
<font color="#FF0000">+ withNormalizedPath<br>
+     "Answer a similar instance to the receiver with a normalized path, i.e., all path navigators such as . or .. will be eliminated."<br>
+ <br>
+     | normalizedParts originalParts |<br>
+     originalParts := self pathParts.<br>
+     normalizedParts := OrderedCollection new: originalParts size.<br>
+     originalParts do: [:part |<br>
+         part<br>
+             caseOf:<br>
+                 {[self class parentDirectoryNickname] -><br>
+                     [normalizedParts size > 1 ifTrue:<br>
+                         [normalizedParts removeLast]].<br>
+                 [self class currentDirectoryNickname] -> []}<br>
+             otherwise: [normalizedParts add: part]].<br>
+     ^ self class forPathParts: normalizedParts</font><br>
<br>
<b>DosFileDirectoryTests>>testWithNormalizedPath {tests} · ct 7/9/2022 19:41</b><br>
<font color="#FF0000">+ testWithNormalizedPath<br>
+ <br>
+     FileDirectory activeDirectoryClass == DosFileDirectory ifFalse:[^self].<br>
+     <br>
+     self assert: 'C:\plonk\griffle' equals: (FileDirectory on: 'C:\plonk\griffle') withNormalizedPath pathName.<br>
+     <br>
+     self assert: 'C:\plonk\griffle' equals: (FileDirectory on: 'C:\.\plonk\.\.\griffle\.\') withNormalizedPath pathName.<br>
+     self assert: 'C:\plonk\wiffy' equals: (FileDirectory on: 'C:\..\..\plonk\..\plonk\griffle\..\wiffy\ziffy\..') withNormalizedPath pathName.<br>
+     <br>
+     self assert: '\\share\ziffy' equals: (FileDirectory on: '\\share\..\plonk\..\ziffy') withNormalizedPath pathName.</font><br>
<br>
<b>FileAbstractSelectionDialog>>directory: {directory tree} · ct 7/9/2022 18:58 (changed)</b><br>
directory: aFileDirectory <br>
    "Set the path of the directory to be displayed in the directory tree pane"<br>
<br>
<s><font color="#0000FF">-     directory := aFileDirectory<br>
</font></s><font color="#FF0000">+     | normalizedDirectory |<br>
+     directory := aFileDirectory.<br>
+     directory ifNil: [^ self].<br>
+     <br>
+     normalizedDirectory := directory withNormalizedPath.<br>
+     normalizedDirectory pathName = directory pathName<br>
+         ifFalse: [directory := normalizedDirectory].<br>
+     self flag: #workaround. "Avoid reassigning the directory as long as possible because PluggableTreeMorph does not perform equality comparison when reselecting items. See comment in #setDirectoryTo:."</font><br>
<br>
<b>FileDirectory class>>forPathParts: {instance creation} · ct 7/9/2022 17:12</b><br>
<font color="#FF0000">+ forPathParts: pathParts<br>
+ <br>
+     | fileName |<br>
+     fileName := String streamContents: [:stream |<br>
+         pathParts do: [:part |<br>
+             stream<br>
+                 nextPutAll: self slash;<br>
+                 nextPutAll: part]].<br>
+     ^ self on: fileName</font><br>
<br>
<b>FileDirectory>>= {comparing} · ct 7/9/2022 17:24 (changed)</b><br>
= aDirectory<br>
    "Compare two FileDirectory instances."<br>
    <br>
    ^(aDirectory isKindOf: FileDirectory) and: [<br>
<font color="#FF0000">+         self flag: #todo. "ct: Should we normalize paths here?"<br>
</font>        (pathName asString <br>
            compare: aDirectory pathName asString <br>
            caseSensitive: (self isCaseSensitive or: [ aDirectory isCaseSensitive ])) = 2 ]<br>
<br>
<b>FileDirectory>>withNormalizedPath {path access} · ct 7/9/2022 19:55</b><br>
<font color="#FF0000">+ withNormalizedPath<br>
+     "Answer a similar instance to the receiver with a normalized path, i.e., all path navigators such as . or .. will be eliminated."<br>
+ <br>
+     | normalizedParts originalParts |<br>
+     originalParts := self pathParts.<br>
+     normalizedParts := OrderedCollection new: originalParts size.<br>
+     originalParts do: [:part |<br>
+         part<br>
+             caseOf:<br>
+                 {[self class parentDirectoryNickname] -><br>
+                     [normalizedParts size > 0 ifTrue:<br>
+                         [normalizedParts removeLast]].<br>
+                 [self class currentDirectoryNickname] -> []}<br>
+             otherwise: [normalizedParts add: part]].<br>
+     ^ self class forPathParts: normalizedParts</font><br>
<br>
<b>UnixFileDirectoryTests>>testWithNormalizedPath {tests} · ct 7/9/2022 19:46</b><br>
<font color="#FF0000">+ testWithNormalizedPath<br>
+ <br>
+     FileDirectory activeDirectoryClass == UnixFileDirectory ifFalse:[^self].<br>
+     <br>
+     self assert: '/media/plonk/griffle' equals: (FileDirectory on: '/media/plonk/griffle') withNormalizedPath pathName.<br>
+     <br>
+     self assert: '/media/plonk/griffle' equals: (FileDirectory on: '/media/./plonk/././griffle/./') withNormalizedPath pathName.<br>
+     self assert: '/media/plonk/wiffy' equals: (FileDirectory on: '/media/../../media/plonk/../plonk/griffle/../wiffy/ziffy/..') withNormalizedPath pathName.</font><br>
<br>
["normalize-paths.2.cs"]<br>
<br>
<font color="#808080">---<br>
</font><font color="#808080"><i>Sent from </i></font><font color="#808080"><i><a href="https://github.com/hpi-swa-lab/squeak-inbox-talk"><u><font color="#808080">Squeak Inbox Talk</font></u></a></i></font><br>
["normalize-paths.2.cs"]