/
RBAddParameterRefactoring.class.st
208 lines (184 loc) · 5.78 KB
/
RBAddParameterRefactoring.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
"
I am a refactoring operations for adding method arguments.
You can modify the method name and add an additional keyword argument and the default value used by senders of the original method. Only one new argument can be added. But you can change the whole method name, as long as the number of argument matches.
For example, for `r:g:b:` add another parameter ""a"" the new method is `r:g:b:a:`
or change the whole method to `setRed:green:blue:alpha:`
This refactoring will
- add a new method with the new argument,
- remove the old method (for all implementors) and
- replace every sender of the prior method with the new one, using the specified default argument.
"
Class {
#name : #RBAddParameterRefactoring,
#superclass : #RBChangeMethodNameRefactoring,
#instVars : [
'initializer',
'senders'
],
#category : #'Refactoring-Core-Refactorings'
}
{ #category : #'instance creation' }
RBAddParameterRefactoring class >> addParameterToMethod: aSelector in: aClass newSelector: newSelector initializer: init [
^self new addParameterToMethod: aSelector
in: aClass
newSelector: newSelector
initializer: init
]
{ #category : #'instance creation' }
RBAddParameterRefactoring class >> addParameterToMethod: aSelector in: aClass newSelector: newSelector permutation: aColl initializer: init [
^self new addParameterToMethod: aSelector
in: aClass
newSelector: newSelector
permutation: aColl
initializer: init
]
{ #category : #'instance creation' }
RBAddParameterRefactoring class >> model: aRBSmalltalk addParameterToMethod: aSelector in: aClass newSelector: newSelector initializer: init [
^(self new)
model: aRBSmalltalk;
addParameterToMethod: aSelector
in: aClass
newSelector: newSelector
initializer: init;
yourself
]
{ #category : #initialization }
RBAddParameterRefactoring >> addParameterToMethod: aSelector in: aClass newSelector: newSel initializer: init [
self
renameMethod: aSelector
in: aClass
to: newSel
permutation: (1 to: newSel numArgs).
initializer := init
]
{ #category : #initialization }
RBAddParameterRefactoring >> addParameterToMethod: aSelector in: aClass newSelector: newSel permutation: aColl initializer: init [
self
renameMethod: aSelector
in: aClass
to: newSel
permutation: aColl.
initializer := init
]
{ #category : #preconditions }
RBAddParameterRefactoring >> checkSendersAccessTo: name [
(#('self' 'super') includes: name) ifTrue: [ ^ self ].
self senders
detect: [ :each | (self canReferenceVariable: name in: each) not ]
ifFound: [ :violatorClass |
self
refactoringError:
('<1s> doesn''t appear to be defined in <2p>'
expandMacrosWith: name
with: violatorClass) ]
]
{ #category : #preconditions }
RBAddParameterRefactoring >> checkVariableReferencesIn: aParseTree [
| searcher |
searcher := self parseTreeSearcher.
searcher
matches: '`var'
do: [ :aNode :answer |
| name |
name := aNode name.
( aNode whoDefines: name ) ifNil: [ self checkSendersAccessTo: name ]
].
searcher executeTree: aParseTree
]
{ #category : #private }
RBAddParameterRefactoring >> modifyImplementorParseTree: parseTree in: aClass [
parseTree
renameSelector: newSelector
andArguments: (permutation value collect: [:e | RBVariableNode named: e ])
]
{ #category : #preconditions }
RBAddParameterRefactoring >> myConditions [
^RBCondition withBlock:
[oldSelector numArgs + 1 = newSelector numArgs
ifFalse:
[self refactoringFailure: newSelector printString
, ' doesn''t have the proper number of arguments.'].
self verifyInitializationExpression.
true]
]
{ #category : #private }
RBAddParameterRefactoring >> newSelectorString [
| stream keywords |
stream := WriteStream on: String new.
keywords := newSelector keywords.
permutation key
doWithIndex:
[:each :index |
stream nextPutAll: (keywords at: index).
each == 0
ifTrue:
[stream
nextPut: $(;
nextPutAll: initializer;
nextPut: $)]
ifFalse:
[stream
nextPutAll: ' ``@arg';
nextPutAll: each printString].
stream nextPut: $ ].
^stream contents
]
{ #category : #private }
RBAddParameterRefactoring >> parseTreeRewriter [
| rewriteRule oldString newString |
rewriteRule := self parseTreeRewriterClass new.
oldString := self buildSelectorString: oldSelector.
newString := self newSelectorString.
rewriteRule replace: '``@object ' , oldString
with: '``@object ' , newString.
^rewriteRule
]
{ #category : #private }
RBAddParameterRefactoring >> safeVariableNameFor: aClass temporaries: allTempVars [
| baseString i newString |
newString := baseString := 'anObject'.
i := 0.
[(allTempVars includes: newString)
or: [aClass definesInstanceVariable: newString]]
whileTrue:
[i := i + 1.
newString := baseString , i printString].
^newString
]
{ #category : #private }
RBAddParameterRefactoring >> senders [
senders
ifNil: [ senders := Set new.
self model allReferencesTo: oldSelector do: [ :each | senders add: each modelClass ]
].
^ senders
]
{ #category : #printing }
RBAddParameterRefactoring >> storeOn: aStream [
aStream nextPut: $(.
self class storeOn: aStream.
aStream
nextPutAll: ' addParameterToMethod: #';
nextPutAll: oldSelector;
nextPutAll: ' in: '.
class storeOn: aStream.
aStream
nextPutAll: ' newSelector: #';
nextPutAll: newSelector;
nextPutAll: ' initializer: ''';
nextPutAll: initializer;
nextPutAll: ''')'
]
{ #category : #preconditions }
RBAddParameterRefactoring >> verifyInitializationExpression [
| tree |
tree := self parserClass
parseExpression: initializer
onError:
[ :msg :index | self refactoringFailure: 'Illegal initialization code because:.' , msg ].
tree isValue
ifFalse: [ self
refactoringFailure:
'The initialization code cannot be a return node or a list of statements' ].
self checkVariableReferencesIn: tree
]