001 /*
002 * Copyright 2010-2014 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.lang.resolve;
018
019 import com.google.common.base.Predicate;
020 import com.google.common.collect.*;
021 import com.intellij.util.containers.ContainerUtil;
022 import kotlin.Function1;
023 import kotlin.Unit;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.annotations.Nullable;
026 import org.jetbrains.jet.lang.descriptors.*;
027 import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorImpl;
028 import org.jetbrains.jet.lang.descriptors.impl.PropertyAccessorDescriptorImpl;
029 import org.jetbrains.jet.lang.descriptors.impl.PropertyDescriptorImpl;
030 import org.jetbrains.jet.lang.resolve.name.Name;
031 import org.jetbrains.jet.lang.types.JetType;
032 import org.jetbrains.jet.lang.types.TypeConstructor;
033 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
034
035 import java.util.*;
036
037 import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.CONFLICT;
038 import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE;
039
040 public class OverridingUtil {
041
042 private static final List<ExternalOverridabilityCondition> EXTERNAL_CONDITIONS =
043 ContainerUtil.collect(ServiceLoader.load(
044 ExternalOverridabilityCondition.class,
045 ExternalOverridabilityCondition.class.getClassLoader()).iterator()
046 );
047
048 private OverridingUtil() {
049 }
050
051 @NotNull
052 public static OverrideCompatibilityInfo isOverridableBy(@NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
053 if (superDescriptor instanceof FunctionDescriptor) {
054 if (!(subDescriptor instanceof FunctionDescriptor)) return OverrideCompatibilityInfo.memberKindMismatch();
055 }
056 else if (superDescriptor instanceof PropertyDescriptor) {
057 if (!(subDescriptor instanceof PropertyDescriptor)) return OverrideCompatibilityInfo.memberKindMismatch();
058 }
059 else {
060 throw new IllegalArgumentException("This type of CallableDescriptor cannot be checked for overridability: " + superDescriptor);
061 }
062
063 // TODO: check outside of this method
064 if (!superDescriptor.getName().equals(subDescriptor.getName())) {
065 return OverrideCompatibilityInfo.nameMismatch();
066 }
067
068 return isOverridableByImpl(superDescriptor, subDescriptor, true);
069 }
070
071 private static List<JetType> compiledValueParameters(CallableDescriptor callableDescriptor) {
072 ReceiverParameterDescriptor receiverParameter = callableDescriptor.getReceiverParameter();
073 ArrayList<JetType> parameters = new ArrayList<JetType>();
074 if (receiverParameter != null) {
075 parameters.add(receiverParameter.getType());
076 }
077 for (ValueParameterDescriptor valueParameterDescriptor : callableDescriptor.getValueParameters()) {
078 parameters.add(valueParameterDescriptor.getType());
079 }
080 return parameters;
081 }
082
083 /**
084 * @param forOverride true for override, false for overload
085 */
086 static OverrideCompatibilityInfo isOverridableByImpl(@NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor, boolean forOverride) {
087
088 // TODO : Visibility
089
090 if ((superDescriptor.getReceiverParameter() == null) != (subDescriptor.getReceiverParameter() == null)) {
091 return OverrideCompatibilityInfo.receiverPresenceMismatch();
092 }
093
094 if (superDescriptor.getValueParameters().size() != subDescriptor.getValueParameters().size()) {
095 return OverrideCompatibilityInfo.valueParameterNumberMismatch();
096 }
097
098 List<JetType> superValueParameters = compiledValueParameters(superDescriptor);
099 List<JetType> subValueParameters = compiledValueParameters(subDescriptor);
100
101 if (forOverride) {
102 if (superDescriptor.getTypeParameters().size() != subDescriptor.getTypeParameters().size()) {
103 for (int i = 0; i < superValueParameters.size(); ++i) {
104 JetType superValueParameterType = getUpperBound(superValueParameters.get(i));
105 JetType subValueParameterType = getUpperBound(subValueParameters.get(i));
106 // TODO: compare erasure
107 if (!JetTypeChecker.INSTANCE.equalTypes(superValueParameterType, subValueParameterType)) {
108 return OverrideCompatibilityInfo.typeParameterNumberMismatch();
109 }
110 }
111 return OverrideCompatibilityInfo.valueParameterTypeMismatch(null, null, OverrideCompatibilityInfo.Result.CONFLICT);
112 }
113 }
114
115 if (forOverride) {
116
117 List<TypeParameterDescriptor> superTypeParameters = superDescriptor.getTypeParameters();
118 List<TypeParameterDescriptor> subTypeParameters = subDescriptor.getTypeParameters();
119
120 BiMap<TypeConstructor, TypeConstructor> axioms = HashBiMap.create();
121 for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) {
122 TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
123 TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);
124 axioms.put(superTypeParameter.getTypeConstructor(), subTypeParameter.getTypeConstructor());
125 }
126
127 for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) {
128 TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
129 TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);
130
131 if (!JetTypeChecker.INSTANCE.equalTypes(superTypeParameter.getUpperBoundsAsType(), subTypeParameter.getUpperBoundsAsType(), axioms)) {
132 return OverrideCompatibilityInfo.boundsMismatch(superTypeParameter, subTypeParameter);
133 }
134 }
135
136 for (int i = 0, unsubstitutedValueParametersSize = superValueParameters.size(); i < unsubstitutedValueParametersSize; i++) {
137 JetType superValueParameter = superValueParameters.get(i);
138 JetType subValueParameter = subValueParameters.get(i);
139
140 boolean bothErrors = superValueParameter.isError() && subValueParameter.isError();
141 if (!bothErrors && !JetTypeChecker.INSTANCE.equalTypes(superValueParameter, subValueParameter, axioms)) {
142 return OverrideCompatibilityInfo.valueParameterTypeMismatch(superValueParameter, subValueParameter, OverrideCompatibilityInfo.Result.INCOMPATIBLE);
143 }
144 }
145
146 for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
147 if (!externalCondition.isOverridable(superDescriptor, subDescriptor)) {
148 return OverrideCompatibilityInfo.externalConditionFailed(externalCondition.getClass());
149 }
150 }
151 }
152 else {
153
154 for (int i = 0; i < superValueParameters.size(); ++i) {
155 JetType superValueParameterType = getUpperBound(superValueParameters.get(i));
156 JetType subValueParameterType = getUpperBound(subValueParameters.get(i));
157 // TODO: compare erasure
158 if (!JetTypeChecker.INSTANCE.equalTypes(superValueParameterType, subValueParameterType)) {
159 return OverrideCompatibilityInfo.valueParameterTypeMismatch(superValueParameterType, subValueParameterType, OverrideCompatibilityInfo.Result.INCOMPATIBLE);
160 }
161 }
162
163 return OverrideCompatibilityInfo.success();
164
165 }
166
167 // TODO : Default values, varargs etc
168
169 return OverrideCompatibilityInfo.success();
170 }
171
172 private static JetType getUpperBound(JetType type) {
173 if (type.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor) {
174 return type;
175 }
176 else if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
177 return ((TypeParameterDescriptor) type.getConstructor().getDeclarationDescriptor()).getUpperBoundsAsType();
178 }
179 else {
180 throw new IllegalStateException("unknown type constructor: " + type.getConstructor().getClass().getName());
181 }
182 }
183
184 public static void bindOverride(CallableMemberDescriptor fromCurrent, CallableMemberDescriptor fromSupertype) {
185 fromCurrent.addOverriddenDescriptor(fromSupertype);
186
187 for (ValueParameterDescriptor parameterFromCurrent : fromCurrent.getValueParameters()) {
188 assert parameterFromCurrent.getIndex() < fromSupertype.getValueParameters().size()
189 : "An override relation between functions implies that they have the same number of value parameters";
190 ValueParameterDescriptor parameterFromSupertype = fromSupertype.getValueParameters().get(parameterFromCurrent.getIndex());
191 parameterFromCurrent.addOverriddenDescriptor(parameterFromSupertype);
192 }
193 }
194
195 public static void generateOverridesInFunctionGroup(
196 @SuppressWarnings("UnusedParameters")
197 @NotNull Name name, //DO NOT DELETE THIS PARAMETER: needed to make sure all descriptors have the same name
198 @NotNull Collection<? extends CallableMemberDescriptor> membersFromSupertypes,
199 @NotNull Collection<? extends CallableMemberDescriptor> membersFromCurrent,
200 @NotNull ClassDescriptor current,
201 @NotNull DescriptorSink sink
202 ) {
203 Collection<CallableMemberDescriptor> notOverridden = Sets.newLinkedHashSet(membersFromSupertypes);
204
205 for (CallableMemberDescriptor fromCurrent : membersFromCurrent) {
206 Collection<CallableMemberDescriptor> bound =
207 extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes, current, sink);
208 notOverridden.removeAll(bound);
209 }
210
211 createAndBindFakeOverrides(current, notOverridden, sink);
212 }
213
214 private static Collection<CallableMemberDescriptor> extractAndBindOverridesForMember(
215 @NotNull CallableMemberDescriptor fromCurrent,
216 @NotNull Collection<? extends CallableMemberDescriptor> descriptorsFromSuper,
217 @NotNull ClassDescriptor current,
218 @NotNull DescriptorSink sink
219 ) {
220 Collection<CallableMemberDescriptor> bound = Lists.newArrayList();
221 for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) {
222 OverrideCompatibilityInfo.Result result = isOverridableBy(fromSupertype, fromCurrent).getResult();
223
224 boolean isVisible = Visibilities.isVisible(fromSupertype, current);
225 switch (result) {
226 case OVERRIDABLE:
227 if (isVisible) {
228 bindOverride(fromCurrent, fromSupertype);
229 }
230 bound.add(fromSupertype);
231 break;
232 case CONFLICT:
233 if (isVisible) {
234 sink.conflict(fromSupertype, fromCurrent);
235 }
236 bound.add(fromSupertype);
237 break;
238 case INCOMPATIBLE:
239 break;
240 }
241 }
242 return bound;
243 }
244
245 private static void createAndBindFakeOverrides(
246 @NotNull ClassDescriptor current,
247 @NotNull Collection<CallableMemberDescriptor> notOverridden,
248 @NotNull DescriptorSink sink
249 ) {
250 Queue<CallableMemberDescriptor> fromSuperQueue = new LinkedList<CallableMemberDescriptor>(notOverridden);
251 while (!fromSuperQueue.isEmpty()) {
252 CallableMemberDescriptor notOverriddenFromSuper = VisibilityUtil.findMemberWithMaxVisibility(fromSuperQueue);
253 Collection<CallableMemberDescriptor> overridables =
254 extractMembersOverridableInBothWays(notOverriddenFromSuper, fromSuperQueue, sink);
255 createAndBindFakeOverride(overridables, current, sink);
256 }
257 }
258
259 private static boolean isMoreSpecific(@NotNull CallableMemberDescriptor a, @NotNull CallableMemberDescriptor b) {
260 if (a instanceof SimpleFunctionDescriptor) {
261 assert b instanceof SimpleFunctionDescriptor : "b is " + b.getClass();
262
263 JetType aReturnType = a.getReturnType();
264 assert aReturnType != null;
265 JetType bReturnType = b.getReturnType();
266 assert bReturnType != null;
267
268 return JetTypeChecker.INSTANCE.isSubtypeOf(aReturnType, bReturnType);
269 }
270 if (a instanceof PropertyDescriptor) {
271 assert b instanceof PropertyDescriptor : "b is " + b.getClass();
272
273 if (((PropertyDescriptor) a).isVar() || ((PropertyDescriptor) b).isVar()) {
274 return ((PropertyDescriptor) a).isVar();
275 }
276
277 // both vals
278 return JetTypeChecker.INSTANCE.isSubtypeOf(((PropertyDescriptor) a).getType(), ((PropertyDescriptor) b).getType());
279 }
280 throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
281 }
282
283 private static CallableMemberDescriptor selectMostSpecificMemberFromSuper(@NotNull Collection<CallableMemberDescriptor> overridables) {
284 CallableMemberDescriptor result = null;
285 for (CallableMemberDescriptor overridable : overridables) {
286 if (result == null || isMoreSpecific(overridable, result)) {
287 result = overridable;
288 }
289 }
290 return result;
291 }
292
293 private static void createAndBindFakeOverride(
294 @NotNull Collection<CallableMemberDescriptor> overridables,
295 @NotNull ClassDescriptor current,
296 @NotNull DescriptorSink sink
297 ) {
298 Collection<CallableMemberDescriptor> visibleOverridables = filterVisibleFakeOverrides(current, overridables);
299 Modality modality = getMinimalModality(visibleOverridables);
300 boolean allInvisible = visibleOverridables.isEmpty();
301 Collection<CallableMemberDescriptor> effectiveOverridden = allInvisible ? overridables : visibleOverridables;
302 Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED;
303 CallableMemberDescriptor mostSpecific = selectMostSpecificMemberFromSuper(effectiveOverridden);
304 CallableMemberDescriptor fakeOverride =
305 mostSpecific.copy(current, modality, visibility, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false);
306 for (CallableMemberDescriptor descriptor : effectiveOverridden) {
307 bindOverride(fakeOverride, descriptor);
308 }
309 sink.addToScope(fakeOverride);
310 }
311
312 @NotNull
313 private static Modality getMinimalModality(@NotNull Collection<CallableMemberDescriptor> descriptors) {
314 Modality modality = Modality.ABSTRACT;
315 for (CallableMemberDescriptor descriptor : descriptors) {
316 if (descriptor.getModality().compareTo(modality) < 0) {
317 modality = descriptor.getModality();
318 }
319 }
320 return modality;
321 }
322
323 @NotNull
324 private static Collection<CallableMemberDescriptor> filterVisibleFakeOverrides(
325 @NotNull final ClassDescriptor current,
326 @NotNull Collection<CallableMemberDescriptor> toFilter
327 ) {
328 return Collections2.filter(toFilter, new Predicate<CallableMemberDescriptor>() {
329 @Override
330 public boolean apply(@Nullable CallableMemberDescriptor descriptor) {
331 //nested class could capture private member, so check for private visibility added
332 return descriptor != null &&
333 descriptor.getVisibility() != Visibilities.PRIVATE &&
334 Visibilities.isVisible(descriptor, current);
335 }
336 });
337 }
338
339 @NotNull
340 private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays(
341 @NotNull CallableMemberDescriptor overrider,
342 @NotNull Queue<CallableMemberDescriptor> extractFrom,
343 @NotNull DescriptorSink sink
344 ) {
345 Collection<CallableMemberDescriptor> overridable = Lists.newArrayList();
346 overridable.add(overrider);
347 for (Iterator<CallableMemberDescriptor> iterator = extractFrom.iterator(); iterator.hasNext(); ) {
348 CallableMemberDescriptor candidate = iterator.next();
349 if (overrider == candidate) {
350 iterator.remove();
351 continue;
352 }
353
354 OverrideCompatibilityInfo.Result result1 = isOverridableBy(candidate, overrider).getResult();
355 OverrideCompatibilityInfo.Result result2 = isOverridableBy(overrider, candidate).getResult();
356 if (result1 == OVERRIDABLE && result2 == OVERRIDABLE) {
357 overridable.add(candidate);
358 iterator.remove();
359 }
360 else if (result1 == CONFLICT || result2 == CONFLICT) {
361 sink.conflict(overrider, candidate);
362 iterator.remove();
363 }
364 }
365 return overridable;
366 }
367
368 public static void resolveUnknownVisibilityForMember(
369 @NotNull CallableMemberDescriptor memberDescriptor,
370 @Nullable Function1<CallableMemberDescriptor, Unit> cannotInferVisibility
371 ) {
372 for (CallableMemberDescriptor descriptor : memberDescriptor.getOverriddenDescriptors()) {
373 if (descriptor.getVisibility() == Visibilities.INHERITED) {
374 resolveUnknownVisibilityForMember(descriptor, cannotInferVisibility);
375 }
376 }
377
378 if (memberDescriptor.getVisibility() != Visibilities.INHERITED) {
379 return;
380 }
381
382 Visibility maxVisibility = computeVisibilityToInherit(memberDescriptor);
383 Visibility visibilityToInherit;
384 if (maxVisibility == null) {
385 if (cannotInferVisibility != null) {
386 cannotInferVisibility.invoke(memberDescriptor);
387 }
388 visibilityToInherit = Visibilities.PUBLIC;
389 }
390 else {
391 visibilityToInherit = maxVisibility;
392 }
393
394 if (memberDescriptor instanceof PropertyDescriptorImpl) {
395 ((PropertyDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
396 for (PropertyAccessorDescriptor accessor : ((PropertyDescriptor) memberDescriptor).getAccessors()) {
397 // If we couldn't infer visibility for property, the diagnostic is already reported, no need to report it again on accessors
398 resolveUnknownVisibilityForMember(accessor, maxVisibility == null ? null : cannotInferVisibility);
399 }
400 }
401 else if (memberDescriptor instanceof FunctionDescriptorImpl) {
402 ((FunctionDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
403 }
404 else {
405 assert memberDescriptor instanceof PropertyAccessorDescriptorImpl;
406 ((PropertyAccessorDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
407 }
408 }
409
410 @Nullable
411 private static Visibility computeVisibilityToInherit(@NotNull CallableMemberDescriptor memberDescriptor) {
412 Set<? extends CallableMemberDescriptor> overriddenDescriptors = memberDescriptor.getOverriddenDescriptors();
413 Visibility maxVisibility = findMaxVisibility(overriddenDescriptors);
414 if (maxVisibility == null) {
415 return null;
416 }
417 if (memberDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
418 for (CallableMemberDescriptor overridden : overriddenDescriptors) {
419 // An implementation (a non-abstract overridden member) of a fake override should have the maximum possible visibility
420 if (overridden.getModality() != Modality.ABSTRACT && !overridden.getVisibility().equals(maxVisibility)) {
421 return null;
422 }
423 }
424 return maxVisibility;
425 }
426 return maxVisibility.normalize();
427 }
428
429 @Nullable
430 private static Visibility findMaxVisibility(@NotNull Collection<? extends CallableMemberDescriptor> descriptors) {
431 if (descriptors.isEmpty()) {
432 return Visibilities.INTERNAL;
433 }
434 Visibility maxVisibility = null;
435 for (CallableMemberDescriptor descriptor : descriptors) {
436 Visibility visibility = descriptor.getVisibility();
437 assert visibility != Visibilities.INHERITED : "Visibility should have been computed for " + descriptor;
438 if (maxVisibility == null) {
439 maxVisibility = visibility;
440 continue;
441 }
442 Integer compareResult = Visibilities.compare(visibility, maxVisibility);
443 if (compareResult == null) {
444 maxVisibility = null;
445 }
446 else if (compareResult > 0) {
447 maxVisibility = visibility;
448 }
449 }
450 // TODO: IDEA seems to issue an incorrect warning here
451 //noinspection ConstantConditions
452 if (maxVisibility == null) {
453 return null;
454 }
455 for (CallableMemberDescriptor descriptor : descriptors) {
456 Integer compareResult = Visibilities.compare(maxVisibility, descriptor.getVisibility());
457 if (compareResult == null || compareResult < 0) {
458 return null;
459 }
460 }
461 return maxVisibility;
462 }
463
464 public interface DescriptorSink {
465 void addToScope(@NotNull CallableMemberDescriptor fakeOverride);
466
467 void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent);
468 }
469
470 public static class OverrideCompatibilityInfo {
471
472 public enum Result {
473 OVERRIDABLE,
474 INCOMPATIBLE,
475 CONFLICT,
476 }
477
478 private static final OverrideCompatibilityInfo SUCCESS = new OverrideCompatibilityInfo(Result.OVERRIDABLE, "SUCCESS");
479
480 @NotNull
481 public static OverrideCompatibilityInfo success() {
482 return SUCCESS;
483 }
484
485 @NotNull
486 public static OverrideCompatibilityInfo nameMismatch() {
487 return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "nameMismatch"); // TODO
488 }
489
490 @NotNull
491 public static OverrideCompatibilityInfo typeParameterNumberMismatch() {
492 return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "typeParameterNumberMismatch"); // TODO
493 }
494
495 @NotNull
496 public static OverrideCompatibilityInfo receiverPresenceMismatch() {
497 return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "receiverPresenceMismatch"); // TODO
498 }
499
500 @NotNull
501 public static OverrideCompatibilityInfo valueParameterNumberMismatch() {
502 return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "valueParameterNumberMismatch"); // TODO
503 }
504
505 @NotNull
506 public static OverrideCompatibilityInfo boundsMismatch(TypeParameterDescriptor superTypeParameter, TypeParameterDescriptor subTypeParameter) {
507 return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "boundsMismatch"); // TODO
508 }
509
510 @NotNull
511 public static OverrideCompatibilityInfo valueParameterTypeMismatch(JetType superValueParameter, JetType subValueParameter, Result result) {
512 return new OverrideCompatibilityInfo(result, "valueParameterTypeMismatch"); // TODO
513 }
514
515 @NotNull
516 public static OverrideCompatibilityInfo memberKindMismatch() {
517 return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "memberKindMismatch"); // TODO
518 }
519
520 @NotNull
521 public static OverrideCompatibilityInfo returnTypeMismatch(JetType substitutedSuperReturnType, JetType unsubstitutedSubReturnType) {
522 return new OverrideCompatibilityInfo(Result.CONFLICT, "returnTypeMismatch: " + unsubstitutedSubReturnType + " >< " + substitutedSuperReturnType); // TODO
523 }
524
525 @NotNull
526 public static OverrideCompatibilityInfo varOverriddenByVal() {
527 return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "varOverriddenByVal"); // TODO
528 }
529
530 @NotNull
531 public static OverrideCompatibilityInfo externalConditionFailed(Class<? extends ExternalOverridabilityCondition> conditionClass) {
532 return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "externalConditionFailed: " + conditionClass.getName()); // TODO
533 }
534
535 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
536
537 private final Result overridable;
538 private final String message;
539
540 public OverrideCompatibilityInfo(Result success, String message) {
541 this.overridable = success;
542 this.message = message;
543 }
544
545 public Result getResult() {
546 return overridable;
547 }
548
549 public String getMessage() {
550 return message;
551 }
552 }
553 }