| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 | 
							- Smalltalk current createPackage: 'Helios-Helpers'!
 
- Object subclass: #HLClassifierLink
 
- 	instanceVariableNames: 'next method'
 
- 	package: 'Helios-Helpers'!
 
- !HLClassifierLink commentStamp!
 
- I am an abstract class implementing a link in a `chain of responsibility` pattern.
 
- y subclasses are in charge of classifying a method according to multiple strategies!
 
- !HLClassifierLink methodsFor: 'accessing'!
 
- method
 
- 	^ method
 
- !
 
- method: anObject
 
- 	method := anObject.
 
- 	self next
 
- 		ifNotNil: [ :nextLink | nextLink method: anObject ]
 
- !
 
- next
 
- 	^ next
 
- !
 
- next: anObject
 
- 	next := anObject
 
- ! !
 
- !HLClassifierLink methodsFor: 'private'!
 
- doClassify
 
- 	self subclassResponsibility
 
- ! !
 
- !HLClassifierLink methodsFor: 'protocol'!
 
- classify
 
- 	self next ifNil: [ ^ false ].
 
- 	
 
- 	^ self doClassify
 
- 		ifTrue: [ true ]
 
- 		ifFalse: [ self next execute ]
 
- ! !
 
- HLClassifierLink subclass: #HLAccessorClassifierLink
 
- 	instanceVariableNames: ''
 
- 	package: 'Helios-Helpers'!
 
- !HLAccessorClassifierLink commentStamp!
 
- I am a classifier checking the method selector matches an instance variable name!
 
- !HLAccessorClassifierLink methodsFor: 'private'!
 
- doClassify
 
- 	| names selector |
 
- 	
 
- 	names := method methodClass allInstanceVariableNames.
 
- 	selector := method selector.
 
- 	
 
- 	(selector last = ':')
 
- 		ifTrue: [ "selector might be a setter"
 
- 			selector := selector allButLast ].
 
- 	
 
- 	(names includes: selector)
 
- 		ifFalse: [ ^ false ].
 
- 		
 
- 	method protocol: 'accessing'.
 
- 	^ true.
 
- ! !
 
- HLClassifierLink subclass: #HLImplementorClassifierLink
 
- 	instanceVariableNames: ''
 
- 	package: 'Helios-Helpers'!
 
- !HLImplementorClassifierLink commentStamp!
 
- I am a classifier checking the other implementations of the same selector and choose the protocol the most populated!
 
- !HLImplementorClassifierLink methodsFor: 'private'!
 
- doClassify
 
- 	| currentClass |
 
- 	currentClass := method methodClass.
 
- 	
 
- 	[ currentClass superclass isNil ] whileFalse: [
 
- 		currentClass := currentClass superclass.
 
- 		(currentClass includesSelector: method selector)
 
- 			ifTrue: [ 
 
- 				method protocol: (currentClass >> method selector) protocol.
 
- 				^ true ]].
 
- 	
 
- 	^ false.
 
- ! !
 
- HLClassifierLink subclass: #HLPrefixClassifierLink
 
- 	instanceVariableNames: 'prefixMapping'
 
- 	package: 'Helios-Helpers'!
 
- !HLPrefixClassifierLink commentStamp!
 
- I am classifier checking the method selector to know if it begins with a known prefix!
 
- !HLPrefixClassifierLink methodsFor: 'initialization'!
 
- buildPrefixDictionary
 
- 	prefixMapping := Dictionary new.
 
- 	prefixMapping 
 
- 		at: 'test' put: 'tests';
 
- 	 	at: 'bench' put: 'benchmarking';
 
- 	 	at: 'copy' put: 'copying';
 
- 		at: 'initialize' put: 'initialization';
 
- 		at: 'accept' put: 'visitor';
 
- 		at: 'visit' put: 'visitor';
 
- 		at: 'signal' put: 'signalling';
 
- 		at: 'parse' put: 'parsing';
 
- 		at: 'add' put: 'adding';
 
- 		at: 'is' put: 'testing';
 
- 		at: 'as' put: 'converting';
 
- 		at: 'new' put: 'instance creation'.
 
- !
 
- initialize
 
- 	super initialize.
 
- 	self buildPrefixDictionary
 
- ! !
 
- !HLPrefixClassifierLink methodsFor: 'private'!
 
- doClassify
 
- 	prefixMapping keysAndValuesDo: [ :prefix :protocol |
 
- 		(method selector beginsWith: prefix)
 
- 			ifTrue: [
 
- 				method protocol: protocol.
 
- 				^ true ]].
 
- 	^ false.
 
- ! !
 
- HLClassifierLink subclass: #HLSuperClassClassifierLink
 
- 	instanceVariableNames: ''
 
- 	package: 'Helios-Helpers'!
 
- !HLSuperClassClassifierLink commentStamp!
 
- I am a classifier checking the superclass chain to find a matching selector!
 
- !HLSuperClassClassifierLink methodsFor: 'private'!
 
- doClassify
 
- 	| protocolBag methods protocolToUse counter |
 
- 	
 
- 	protocolBag := Dictionary new.
 
- 	methods := HLReferencesModel new implementorsOf: method selector.
 
- 	methods
 
- 		ifEmpty: [ ^ false ]
 
- 		ifNotEmpty: [
 
- 			methods 
 
- 				do: [ :aMethod || protocol |
 
- 					protocol := aMethod method protocol.
 
- 					(method methodClass = aMethod methodClass)
 
- 						ifFalse: [
 
- 						((protocol first = '*') or: [ protocol = method defaultProtocol ])
 
- 							ifFalse: [ 
 
- 								protocolBag 
 
- 									at: protocol 
 
- 									put: (protocolBag at: protocol ifAbsent: [ 0 ]) + 1 ] ] ] ].
 
- 			
 
- 	protocolBag ifEmpty: [ ^ false ].
 
- 	protocolToUse := nil.
 
- 	counter := 0.
 
- 	protocolBag keysAndValuesDo: [ :key :value | value > counter 
 
- 		ifTrue: [
 
- 			counter := value.
 
- 			protocolToUse := key ] ].
 
- 	method protocol: protocolToUse.
 
- 	^ true
 
- ! !
 
- Object subclass: #HLGenerationOutput
 
- 	instanceVariableNames: 'sourceCodes protocol targetClass'
 
- 	package: 'Helios-Helpers'!
 
- !HLGenerationOutput commentStamp!
 
- I am a simple data object used to store the result of a generation process!
 
- !HLGenerationOutput methodsFor: 'accessing'!
 
- protocol
 
- 	^ protocol
 
- !
 
- protocol: aString
 
- 	protocol := aString
 
- !
 
- sourceCodes
 
- 	^ sourceCodes
 
- !
 
- sourceCodes: aCollection
 
- 	sourceCodes := aCollection
 
- !
 
- targetClass
 
- 	^ targetClass
 
- !
 
- targetClass: aClass
 
- 	targetClass := aClass
 
- ! !
 
- !HLGenerationOutput methodsFor: 'initialization'!
 
- initialize
 
- 	super initialize.
 
- 	
 
- 	sourceCodes := OrderedCollection new
 
- ! !
 
- !HLGenerationOutput methodsFor: 'protocol'!
 
- addSourceCode: aString
 
- 	sourceCodes add: aString
 
- !
 
- compile
 
- 	sourceCodes do: [ :methodSourceCode |
 
- 		(targetClass includesSelector: methodSourceCode selector)
 
- 			ifFalse: [ 
 
- 				targetClass 
 
- 					compile: methodSourceCode sourceCode
 
- 					protocol: protocol ] ]
 
- ! !
 
- HLGenerationOutput subclass: #HLGenerationOutputWithIndex
 
- 	instanceVariableNames: 'index'
 
- 	package: 'Helios-Helpers'!
 
- !HLGenerationOutputWithIndex commentStamp!
 
- I am a simple data object used to store the result of a generation process.
 
- In addition of my super class, I have an index where to put the cursor at the end of the process for the first method created (aka. the first in `sourceCodes`)!
 
- !HLGenerationOutputWithIndex methodsFor: 'accessing'!
 
- index
 
- 	^ index
 
- !
 
- index: anIndex
 
- 	index := anIndex
 
- ! !
 
- Object subclass: #HLGenerator
 
- 	instanceVariableNames: 'output'
 
- 	package: 'Helios-Helpers'!
 
- !HLGenerator commentStamp!
 
- I am the abstract super class of the generators.
 
- My main method is `generate` which produce an `output` object!
 
- !HLGenerator methodsFor: 'accessing'!
 
- class: aClass
 
- 	output targetClass: aClass
 
- !
 
- output
 
- 	^ output
 
- ! !
 
- !HLGenerator methodsFor: 'initialization'!
 
- initialize
 
- 	super initialize.
 
- 	
 
- 	output := HLGenerationOutput new
 
- ! !
 
- !HLGenerator methodsFor: 'protocol'!
 
- generate
 
- 	output targetClass ifNil: [ self error: 'class should not be nil'].
 
- ! !
 
- HLGenerator subclass: #HLAccessorsGenerator
 
- 	instanceVariableNames: ''
 
- 	package: 'Helios-Helpers'!
 
- !HLAccessorsGenerator commentStamp!
 
- I am a generator used to compile the getters/setters of a class!
 
- !HLAccessorsGenerator methodsFor: 'double-dispatch'!
 
- accessorProtocolForObject
 
- 	output protocol: 'accessing'
 
- !
 
- accessorsSourceCodesForObject
 
- 	| sources |
 
- 	
 
- 	sources := OrderedCollection new.
 
- 	output targetClass instanceVariableNames sorted do: [ :each | 
 
- 		sources 
 
- 			add: (self getterFor: each);
 
- 			add: (self setterFor: each) ].
 
- 	output sourceCodes: sources
 
- ! !
 
- !HLAccessorsGenerator methodsFor: 'private'!
 
- getterFor: anInstanceVariable
 
- 	^ HLMethodSourceCode new
 
- 		selector:anInstanceVariable;
 
- 		sourceCode: (String streamContents: [ :stream |
 
- 		stream << anInstanceVariable.
 
- 		stream cr tab.
 
- 		stream << '^ ' << anInstanceVariable ])
 
- !
 
- setterFor: anInstanceVariable
 
- 	^ HLMethodSourceCode new
 
- 		selector: anInstanceVariable, ':';
 
- 		sourceCode: (String streamContents: [ :stream |
 
- 		stream << anInstanceVariable << ': anObject'.
 
- 		stream cr tab.
 
- 		stream << anInstanceVariable << ' := anObject' ])
 
- ! !
 
- !HLAccessorsGenerator methodsFor: 'protocol'!
 
- generate
 
- 	super generate.
 
- 	
 
- 	output targetClass 
 
- 		accessorsSourceCodesWith: self;
 
- 		accessorProtocolWith: self
 
- ! !
 
- HLGenerator subclass: #HLInitializeGenerator
 
- 	instanceVariableNames: ''
 
- 	package: 'Helios-Helpers'!
 
- !HLInitializeGenerator commentStamp!
 
- I am used to double-dispatch the `initialize` method(s) generation.
 
- Usage:
 
-     ^ HLInitializeGenerator new
 
-         class: aClass;
 
-         generate;
 
-         output
 
- I am a disposable object!
 
- !HLInitializeGenerator methodsFor: 'double-dispatch'!
 
- initializeForObject
 
- 	output addSourceCode: self initializeCodeForObject
 
- !
 
- initializeIndexForObject
 
- 	output index: self computeIndexForObject
 
- !
 
- initializeProtocolForObject
 
- 	output protocol: self retrieveProtocolForObject
 
- ! !
 
- !HLInitializeGenerator methodsFor: 'initialization'!
 
- initialize
 
- 	super initialize.
 
- 	
 
- 	output := HLGenerationOutputWithIndex new
 
- ! !
 
- !HLInitializeGenerator methodsFor: 'private'!
 
- computeIndexForObject
 
- 	| instVars headerSize firstInstVarSize |
 
- 	
 
- 	"32 is the size of the `initiliaze super initialize` part"
 
- 	headerSize := 32.
 
- 	instVars := output targetClass instanceVariableNames.
 
- 	firstInstVarSize := instVars sorted
 
- 		ifEmpty: [ 0 ]
 
- 		ifNotEmpty:[ instVars first size + 4 ].
 
- 	^ headerSize + firstInstVarSize
 
- !
 
- generateInitializeCodeForObject	
 
- 	^ String streamContents: [ :str || instVars size |
 
- 		instVars := output targetClass instanceVariableNames sorted.
 
- 		size := instVars size.
 
- 		str << 'initialize'.
 
- 		str cr tab << 'super initialize.';cr.
 
- 		str cr tab.
 
- 		instVars withIndexDo: [ :name :index |
 
- 			index ~= 1 ifTrue: [ str cr tab ].
 
- 			str << name << ' := nil'.
 
- 			index ~= size ifTrue: [ str << '.' ] ] ].
 
- !
 
- initializeCodeForObject	
 
- 	^ HLMethodSourceCode new
 
- 		selector: 'initialize';
 
- 		sourceCode: self generateInitializeCodeForObject;
 
- 		yourself
 
- !
 
- retrieveProtocolForObject
 
- 	^ 'initialization'
 
- ! !
 
- !HLInitializeGenerator methodsFor: 'protocol'!
 
- generate
 
- 	super generate.
 
- 	
 
- 	output targetClass 
 
- 		initializeSourceCodesWith: self;
 
- 		initializeIndexWith: self;
 
- 		initializeProtocolWith: self
 
- ! !
 
- Object subclass: #HLMethodClassifier
 
- 	instanceVariableNames: 'firstLink'
 
- 	package: 'Helios-Helpers'!
 
- !HLMethodClassifier commentStamp!
 
- I am in charge of categorizing methods following this strategy:
 
- - is it an accessor?
 
- - is it overriding a superclass method?
 
- - is it starting with a know prefix?
 
- - how are categorized the other implementations?!
 
- !HLMethodClassifier methodsFor: 'initialization'!
 
- buildChainOfResponsibility
 
- 	self addLink: HLImplementorClassifierLink new.
 
- 	self addLink: HLPrefixClassifierLink new.
 
- 	self addLink: HLSuperclassClassifierLink new.
 
- 	self addLink: HLAccessorClassifierLink new
 
- !
 
- initialize
 
- 	super initialize.
 
- 	
 
- 	self buildChainOfResponsibility
 
- ! !
 
- !HLMethodClassifier methodsFor: 'private'!
 
- addLink: aLink
 
- 	aLink next: firstLink.
 
- 	firstLink := aLink
 
- ! !
 
- !HLMethodClassifier methodsFor: 'protocol'!
 
- classify: aMethod
 
- 	firstLink
 
- 		method: aMethod;
 
- 		classify
 
- !
 
- classifyAll: aCollectionOfMethods
 
- 	aCollectionOfMethods do: [ :method |
 
- 		self classify: method ]
 
- ! !
 
- Object subclass: #HLMethodSourceCode
 
- 	instanceVariableNames: 'selector sourceCode'
 
- 	package: 'Helios-Helpers'!
 
- !HLMethodSourceCode commentStamp!
 
- I am a simple data object keeping track of the information about a method that will be compiled at the end of the generation process!
 
- !HLMethodSourceCode methodsFor: 'accessing'!
 
- selector
 
- 	^ selector
 
- !
 
- selector: aSelector
 
- 	selector := aSelector
 
- !
 
- sourceCode
 
- 	^ sourceCode
 
- !
 
- sourceCode: aString
 
- 	sourceCode := aString
 
- ! !
 
 
  |