-
-
Notifications
You must be signed in to change notification settings - Fork 354
/
MCFileTreeStCypressReader.class.st
350 lines (316 loc) · 13.1 KB
/
MCFileTreeStCypressReader.class.st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
"
Reader for Cypress format
see https://github.com/CampSmalltalk/Cypress
"
Class {
#name : #MCFileTreeStCypressReader,
#superclass : #MCFileTreeStSnapshotReader,
#category : #'MonticelloFileTree-Core'
}
{ #category : #accessing }
MCFileTreeStCypressReader class >> extension [
^ 'package'
]
{ #category : #accessing }
MCFileTreeStCypressReader class >> monticelloMetaDirName [
^ MCFileTreeStCypressWriter monticelloMetaDirName
]
{ #category : #utilities }
MCFileTreeStCypressReader >> addClassAndMethodDefinitionsFromDirectory: aDirectory [
aDirectory entries
do: [ :entry |
(entry name endsWith: '.trait')
ifTrue: [ self addTraitAndMethodDefinitionsFromEntry: entry ].
(entry name endsWith: '.class')
ifTrue: [ self addClassAndMethodDefinitionsFromEntry: entry ].
(entry name endsWith: '.extension')
ifTrue: [ self addExtensionClassAndMethodDefinitionsFromEntry: entry ] ]
]
{ #category : #utilities }
MCFileTreeStCypressReader >> addClassAndMethodDefinitionsFromEntry: classEntry [
| classDirectory classPropertiesDict classComment entries methodPropertiesDict |
classDirectory := self fileUtils directoryFromEntry: classEntry.
entries := classDirectory entries.
entries
detect: [ :entry | self isPropertyFile: entry ]
ifFound: [ :propertyEntry |
propertyEntry
readStreamDo: [ :fileStream | classPropertiesDict := STON fromStream: fileStream ] ].
entries
detect: [ :entry | entry name = 'README.md' ]
ifFound: [ :commentEntry |
commentEntry
readStreamDo: [ :fileStream | classComment := fileStream contents ] ].
methodPropertiesDict := Dictionary new.
entries
detect: [ :entry | self isMethodPropertyFile: entry ]
ifFound: [ :propertyEntry |
propertyEntry
readStreamDo:
[ :fileStream | "Issue 33: https://github.com/dalehenrich/filetree/issues/33" methodPropertiesDict := STON fromStream: fileStream ] ].
self
addClassDefinitionFrom: classPropertiesDict
comment: classComment withSqueakLineEndings.
self
addMethodDefinitionsForClass: (classPropertiesDict at: 'name')
methodProperties: methodPropertiesDict
in: entries
]
{ #category : #utilities }
MCFileTreeStCypressReader >> addClassDefinitionFrom: classPropertiesDict comment: classComment [
| categoryName className |
className := classPropertiesDict at: 'name'.
categoryName := classPropertiesDict
at: 'category'
ifAbsent: [ self packageNameFromPackageDirectory ].
self validateClassCategory: categoryName for: className.
definitions
add:
(MCClassDefinition
name: className
superclassName: (classPropertiesDict at: 'super')
traitComposition: (classPropertiesDict at: 'traitcomposition' ifAbsent: [ '{}' ])
classTraitComposition: (classPropertiesDict at: 'classtraitcomposition' ifAbsent: [ '{}' ])
category: categoryName
instVarNames: (classPropertiesDict at: 'instvars' ifAbsent: [ #() ])
classVarNames: (classPropertiesDict at: 'classvars' ifAbsent: [ #() ])
poolDictionaryNames: (classPropertiesDict at: 'pools' ifAbsent: [ #() ])
classInstVarNames:
(classPropertiesDict at: 'classinstvars' ifAbsent: [ #() ])
type: (classPropertiesDict at: 'type' ifAbsent: [ 'normal' ]) asSymbol
comment: classComment
commentStamp: (classPropertiesDict at: 'commentStamp' ifAbsent: [ '' ]))
]
{ #category : #utilities }
MCFileTreeStCypressReader >> addExtensionClassAndMethodDefinitionsFromEntry: classEntry [
| classDirectory classPropertiesDict methodPropertiesDict entries |
classDirectory := self fileUtils directoryFromEntry: classEntry.
entries := classDirectory entries.
entries
detect: [ :entry | self isPropertyFile: entry ]
ifFound: [ :propertyEntry |
propertyEntry
readStreamDo: [ :fileStream | classPropertiesDict := STON fromStream: fileStream ] ].
methodPropertiesDict := Dictionary new.
entries
detect: [ :entry | self isMethodPropertyFile: entry ]
ifFound: [ :propertyEntry |
propertyEntry
readStreamDo:
[ :fileStream | "Issue 33: https://github.com/dalehenrich/filetree/issues/33" methodPropertiesDict := STON fromStream: fileStream ] ].
self
addMethodDefinitionsForClass: (classPropertiesDict at: 'name')
methodProperties: methodPropertiesDict
in: entries
extensionMethod: true
]
{ #category : #utilities }
MCFileTreeStCypressReader >> addMethodDefinitionsForClass: className methodProperties: methodProperties in: entries [
^ self
addMethodDefinitionsForClass: className
methodProperties: methodProperties
in: entries
extensionMethod: false
]
{ #category : #utilities }
MCFileTreeStCypressReader >> addMethodDefinitionsForClass: className methodProperties: methodProperties in: entries extensionMethod: extensionMethod [
entries
do: [ :entry |
| classIsMeta |
classIsMeta := false.
entry name = 'class' ifTrue: [ classIsMeta := true ].
(entry name = 'instance' or: [ entry name = 'class' ])
ifTrue: [ (self loadableDefinitionsFrom: (self fileUtils directoryFromEntry: entry))
do: [ :methodEntry |
methodEntry
readStreamDo: [ :fileStream |
| category source timestamp selector |
category := fileStream nextLine.
source := fileStream upToEnd.
selector := self methodSelectorFor: source.
timestamp := methodProperties at: (classIsMeta ifTrue: [ 'class' ] ifFalse: [ 'instance' ]) ifPresent: [ :map | map at: selector asString ifAbsent: [ ] ]. "Issue 33: https://github.com/dalehenrich/filetree/issues/33"
timestamp ifNil: [ timestamp := self info author , ' ' , self info date mmddyyyy , ' ' , self info time print24 ].
extensionMethod ifTrue: [ self validateExtensionMethodCategory: category for: className selector: selector ].
definitions
add:
(MCMethodDefinition
className: className
classIsMeta: classIsMeta
selector: selector
category: category
timeStamp: timestamp
source: source) ] ] ] ]
]
{ #category : #utilities }
MCFileTreeStCypressReader >> addTraitAndMethodDefinitionsFromEntry: classEntry [
| classDirectory classPropertiesDict classComment entries methodPropertiesDict |
classDirectory := self fileUtils directoryFromEntry: classEntry.
entries := classDirectory entries.
entries
detect: [ :entry | self isPropertyFile: entry ]
ifFound: [ :propertyEntry |
propertyEntry
readStreamDo: [ :fileStream | classPropertiesDict := STON fromStream: fileStream ] ].
entries
detect: [ :entry | entry name = 'README.md' ]
ifFound: [ :commentEntry |
commentEntry
readStreamDo: [ :fileStream | classComment := fileStream contents ] ]
ifNone: [ classComment := '' ].
methodPropertiesDict := Dictionary new.
entries
detect: [ :entry | self isMethodPropertyFile: entry ]
ifFound: [ :propertyEntry |
propertyEntry
readStreamDo:
[ :fileStream | "Issue 33: https://github.com/dalehenrich/filetree/issues/33" methodPropertiesDict := STON fromStream: fileStream ] ].
self
addTraitDefinitionFrom: classPropertiesDict
comment: classComment withSqueakLineEndings.
self
addMethodDefinitionsForClass: (classPropertiesDict at: 'name')
methodProperties: methodPropertiesDict
in: entries
]
{ #category : #utilities }
MCFileTreeStCypressReader >> addTraitDefinitionFrom: traitPropertiesDict comment: traitComment [
definitions
add:
(MCTraitDefinition
name: (traitPropertiesDict at: 'name')
traitComposition: (traitPropertiesDict at: 'traitcomposition' ifAbsent: [ '{}' ])
category: (traitPropertiesDict at: 'category' ifAbsent: [ self packageNameFromPackageDirectory ])
instVarNames: (traitPropertiesDict at: 'instvars' ifAbsent: [ #() ])
comment: traitComment
commentStamp: (traitPropertiesDict at: 'commentStamp' ifAbsent: [ '' ])).
traitPropertiesDict at: 'classtraitcomposition' ifPresent: [:classTraitComposition |
definitions
add:
(MCClassTraitDefinition
baseTraitName: (traitPropertiesDict at: 'name')
classTraitComposition: classTraitComposition)].
]
{ #category : #accessing }
MCFileTreeStCypressReader >> basicVersion [
self hasMonticelloMetadata
ifTrue: [ ^ super basicVersion ].
^ MCVersion new
setPackage: self package
info: self info
snapshot: self snapshot
dependencies: #();
yourself
]
{ #category : #private }
MCFileTreeStCypressReader >> isMethodPropertyFile: entry [
^ entry name = 'methodProperties.ston' or: [ entry name = 'methodProperties.json']
]
{ #category : #private }
MCFileTreeStCypressReader >> isPropertyFile: entry [
^ entry name = 'properties.ston' or: [ entry name = 'properties.json']
]
{ #category : #utilities }
MCFileTreeStCypressReader >> loadDefinitions [
| entries directory |
definitions := OrderedCollection new.
directory := self fileUtils directoryFromPath: self monticelloMetaDirName relativeTo: packageDirectory.
(self fileUtils directoryExists: directory)
ifTrue: [
entries := directory entries.
self
addDefinitionFromFile: (entries detect: [ :entry | entry name beginsWith: 'categories' ] ifNone: [ ])
inDirectory: directory ]
ifFalse: [definitions add: (MCOrganizationDefinition categories: {self packageNameFromPackageDirectory }) ].
self addClassAndMethodDefinitionsFromDirectory: packageDirectory.
(self fileUtils directoryExists: directory)
ifTrue: [
self
addDefinitionFromFile: (entries detect: [ :entry | entry name beginsWith: 'initializers' ] ifNone: [ ])
inDirectory: directory ]
]
{ #category : #accessing }
MCFileTreeStCypressReader >> loadPackage [
self hasMonticelloMetadata
ifTrue: [ ^ super loadPackage ].
package := MCPackage named: self packageNameFromPackageDirectory
]
{ #category : #accessing }
MCFileTreeStCypressReader >> loadVersionInfo [
self hasMonticelloMetadata
ifTrue: [ ^ info := self extractInfoFrom: (self parseMember: 'version') ].
info := MCVersionInfo
name: self packageNameFromPackageDirectory , '-cypress.1'
id: UUID new
message: 'fabricated from a Cypress format repository'
date: Date today
time: Time now
author: ''
ancestors: #()
stepChildren: #()
]
{ #category : #utilities }
MCFileTreeStCypressReader >> methodSelectorFor: source [
^ Object compilerClass new parseSelector: source
]
{ #category : #accessing }
MCFileTreeStCypressReader >> packageNameFromPackageDirectory [
| filename2 |
filename2 := self fileUtils directoryName: packageDirectory.
^ filename2 copyFrom: 1 to: (filename2 lastIndexOf: $.) - 1
]
{ #category : #validation }
MCFileTreeStCypressReader >> validateClassCategory: categoryName for: className [
"https://github.com/dalehenrich/filetree/issues/136"
"class category must match the package name ... guard against manual editing mistakes"
| prefix |
prefix := self packageNameFromPackageDirectory.
(self verifyCategory: categoryName matches: prefix)
ifTrue: [ ^ self ].
self
error:
'Class category name ' , categoryName printString , ' for the class '
, className printString , ' is inconsistent with the package name '
, prefix printString
]
{ #category : #validation }
MCFileTreeStCypressReader >> validateExtensionMethodCategory: categoryName for: className selector: selector [
"https://github.com/dalehenrich/filetree/issues/136"
"method category must match the package name for extension methods... guard against manual editing mistakes"
"extracted from PackageInfo>>isForeignClassExtension:"
| prefix |
prefix := '*' , self packageNameFromPackageDirectory asLowercase.
categoryName
ifNotNil: [
(categoryName isEmpty not
and: [
categoryName first = $*
and: [
"asLowercase needed in GemStone 3.1.0.6?"
self verifyCategory: categoryName asLowercase matches: prefix ] ])
ifTrue: [ ^ self ] ].
self
error:
'Method protocol ' , categoryName printString , ' for the method '
, selector asString printString , ' in class ' , className printString
, ' is inconsistent with the package name ' , prefix printString
]
{ #category : #validation }
MCFileTreeStCypressReader >> verifyCategory: categoryName matches: basicPackageName [
"https://github.com/dalehenrich/filetree/issues/136"
"copied from PackageInfo>>category:matches: and GoferVersionReference>>parseName:"
| prefixSize catSize packagePrefix |
categoryName ifNil: [ ^ false ].
packagePrefix := basicPackageName.
(packagePrefix includes: $.)
ifTrue: [
"exclude branch name"
packagePrefix := packagePrefix copyUpTo: $. ].
catSize := categoryName size.
prefixSize := packagePrefix size.
catSize < prefixSize
ifTrue: [ ^ false ].
(categoryName findString: packagePrefix startingAt: 1 caseSensitive: false)
= 1
ifFalse: [ ^ false ].
^ (categoryName at: packagePrefix size + 1 ifAbsent: [ ^ true ]) = $-
]