@@ -76,6 +76,10 @@ static void handleContentConflicts(TStar<SpoonNode, RoledValues> delta) {
76
76
77
77
Optional <?> merged = Optional .empty ();
78
78
79
+ // sometimes a value can be partially merged (e.g. modifiers), and then we want to be
80
+ // able to set the merged value, AND flag a conflict.
81
+ boolean conflictPresent = false ;
82
+
79
83
// if either value is equal to base, we keep THE OTHER one
80
84
if (baseValOpt .isPresent () && baseValOpt .get ().equals (leftVal )) {
81
85
merged = Optional .of (rightVal );
@@ -93,10 +97,12 @@ static void handleContentConflicts(TStar<SpoonNode, RoledValues> delta) {
93
97
}
94
98
break ;
95
99
case MODIFIER :
96
- merged = mergeModifierKinds (
100
+ Pair < Boolean , Optional < Set < ModifierKind >>> mergePair = mergeModifierKinds (
97
101
baseValOpt .map (o -> (Set <ModifierKind >) o ),
98
102
(Set <ModifierKind >) leftVal ,
99
103
(Set <ModifierKind >) rightVal );
104
+ conflictPresent = mergePair .first ;
105
+ merged = mergePair .second ;
100
106
break ;
101
107
case COMMENT_CONTENT :
102
108
merged = mergeComments (baseValOpt .orElse ("" ), leftVal , rightVal );
@@ -116,7 +122,9 @@ static void handleContentConflicts(TStar<SpoonNode, RoledValues> delta) {
116
122
117
123
if (merged .isPresent ()) {
118
124
mergedRoledValues .set (i , role , merged .get ());
119
- unresolvedConflicts .pop ();
125
+
126
+ if (!conflictPresent )
127
+ unresolvedConflicts .pop ();
120
128
}
121
129
}
122
130
@@ -167,6 +175,28 @@ static void handleContentConflicts(TStar<SpoonNode, RoledValues> delta) {
167
175
return Triple .of (visibility , keywords , other );
168
176
}
169
177
178
+ /**
179
+ * Separate modifiers into visibility (public, private, protected), keywords (static, final) and all
180
+ * others.
181
+ *
182
+ * @param modifiers A collection of modifiers.
183
+ * @return A triple with visibility in first, keywords in second and other in third.
184
+ */
185
+ public static Triple <Set <ModifierKind >, Set <ModifierKind >, Set <ModifierKind >>
186
+ categorizeModifiers (Collection <ModifierKind > modifiers ) {
187
+ return categorizeModifiers (modifiers .stream ());
188
+ }
189
+
190
+ /**
191
+ * Extract the visibility modifier(s).
192
+ *
193
+ * @param modifiers A collection of modifiers.
194
+ * @return A possibly empty set of visibility modifiers.
195
+ */
196
+ public static Set <ModifierKind > getVisibility (Collection <ModifierKind > modifiers ) {
197
+ return categorizeModifiers (modifiers ).first ;
198
+ }
199
+
170
200
private static Optional <?> mergeIsUpper (Optional <CtElement > baseElem , CtElement leftElem , CtElement rightElem ) {
171
201
CtWildcardReference left = (CtWildcardReference ) leftElem ;
172
202
CtWildcardReference right = (CtWildcardReference ) rightElem ;
@@ -201,16 +231,23 @@ private static Optional<?> mergeComments(Object base, Object left, Object right)
201
231
return Optional .of (merge .first );
202
232
}
203
233
204
- private static Optional <Set <ModifierKind >>
234
+ /**
235
+ * Return a pair (conflict, mergedModifiers).
236
+ * If the conflict value is true, there is a conflict in the visibility modifiers, and the merged value
237
+ * will always be the left one.
238
+ */
239
+ private static Pair <Boolean , Optional <Set <ModifierKind >>>
205
240
mergeModifierKinds (Optional <Set <ModifierKind >> base , Set <ModifierKind > left , Set <ModifierKind > right ) {
206
- // all revisions must have the same primary value
207
-
208
241
Set <ModifierKind > baseModifiers = base .orElseGet (HashSet ::new );
209
242
210
243
Stream <ModifierKind > modifiers = Stream .of (baseModifiers , left , right ).flatMap (Set ::stream );
211
244
Triple <Set <ModifierKind >, Set <ModifierKind >, Set <ModifierKind >>
212
245
categorizedMods = categorizeModifiers (modifiers );
213
246
247
+ Set <ModifierKind > baseVis = getVisibility (baseModifiers );
248
+ Set <ModifierKind > leftVis = getVisibility (left );
249
+ Set <ModifierKind > rightVis = getVisibility (right );
250
+
214
251
Set <ModifierKind > visibility = categorizedMods .first ;
215
252
Set <ModifierKind > keywords = categorizedMods .second ;
216
253
Set <ModifierKind > other = categorizedMods .third ;
@@ -221,8 +258,12 @@ private static Optional<?> mergeComments(Object base, Object left, Object right)
221
258
222
259
// visibility is the only place where we can have obvious addition conflicts
223
260
// TODO further analyze conflicts among other modifiers (e.g. you can't combine static and volatile)
224
- if (visibility .size () != 1 ) {
225
- return Optional .empty ();
261
+ boolean conflict = visibility .size () != 1 ||
262
+ !leftVis .equals (rightVis ) && !leftVis .equals (baseVis ) && !rightVis .equals (baseVis );
263
+
264
+ if (conflict ) {
265
+ // use left version on conflict to follow the convention
266
+ visibility = leftVis ;
226
267
}
227
268
228
269
Set <ModifierKind > mods = Stream .of (visibility , keywords , other ).flatMap (Set ::stream )
@@ -235,7 +276,7 @@ private static Optional<?> mergeComments(Object base, Object left, Object right)
235
276
)
236
277
.collect (Collectors .toSet ());
237
278
238
- return Optional .of (mods );
279
+ return Pair . of ( conflict , Optional .of (mods ) );
239
280
}
240
281
241
282
private static _ContentTriple getContentRevisions (Set <Content <SpoonNode , RoledValues >> contents ) {
0 commit comments