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                @NotNull 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 visibilityToInherit = computeVisibilityToInherit(memberDescriptor, cannotInferVisibility);
383            if (memberDescriptor instanceof PropertyDescriptorImpl) {
384                ((PropertyDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
385                for (PropertyAccessorDescriptor accessor : ((PropertyDescriptor) memberDescriptor).getAccessors()) {
386                    resolveUnknownVisibilityForMember(accessor, cannotInferVisibility);
387                }
388            }
389            else if (memberDescriptor instanceof FunctionDescriptorImpl) {
390                ((FunctionDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
391            }
392            else {
393                assert memberDescriptor instanceof PropertyAccessorDescriptorImpl;
394                ((PropertyAccessorDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
395            }
396        }
397    
398        @NotNull
399        private static Visibility computeVisibilityToInherit(
400                @NotNull CallableMemberDescriptor memberDescriptor,
401                @NotNull Function1<CallableMemberDescriptor, Unit> cannotInferVisibility
402        ) {
403            Visibility maxVisibility = findMaxVisibility(memberDescriptor.getOverriddenDescriptors());
404            if (maxVisibility == null) {
405                cannotInferVisibility.invoke(memberDescriptor);
406                return Visibilities.PUBLIC;
407            }
408            if (memberDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
409                return maxVisibility;
410            }
411            return maxVisibility.normalize();
412        }
413    
414        @Nullable
415        private static Visibility findMaxVisibility(@NotNull Collection<? extends CallableMemberDescriptor> descriptors) {
416            if (descriptors.isEmpty()) {
417                return Visibilities.INTERNAL;
418            }
419            Visibility maxVisibility = null;
420            for (CallableMemberDescriptor descriptor : descriptors) {
421                Visibility visibility = descriptor.getVisibility();
422                assert visibility != Visibilities.INHERITED;
423                if (maxVisibility == null) {
424                    maxVisibility = visibility;
425                    continue;
426                }
427                Integer compareResult = Visibilities.compare(visibility, maxVisibility);
428                if (compareResult == null) {
429                    maxVisibility = null;
430                }
431                else if (compareResult > 0) {
432                    maxVisibility = visibility;
433                }
434            }
435            // TODO: IDEA seems to issue an incorrect warning here
436            //noinspection ConstantConditions
437            if (maxVisibility == null) {
438                return null;
439            }
440            for (CallableMemberDescriptor descriptor : descriptors) {
441                Integer compareResult = Visibilities.compare(maxVisibility, descriptor.getVisibility());
442                if (compareResult == null || compareResult < 0) {
443                    return null;
444                }
445            }
446            return maxVisibility;
447        }
448    
449        public interface DescriptorSink {
450            void addToScope(@NotNull CallableMemberDescriptor fakeOverride);
451    
452            void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent);
453        }
454    
455        public static class OverrideCompatibilityInfo {
456    
457            public enum Result {
458                OVERRIDABLE,
459                INCOMPATIBLE,
460                CONFLICT,
461            }
462    
463            private static final OverrideCompatibilityInfo SUCCESS = new OverrideCompatibilityInfo(Result.OVERRIDABLE, "SUCCESS");
464    
465            @NotNull
466            public static OverrideCompatibilityInfo success() {
467                return SUCCESS;
468            }
469    
470            @NotNull
471            public static OverrideCompatibilityInfo nameMismatch() {
472                return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "nameMismatch"); // TODO
473            }
474    
475            @NotNull
476            public static OverrideCompatibilityInfo typeParameterNumberMismatch() {
477                return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "typeParameterNumberMismatch"); // TODO
478            }
479    
480            @NotNull
481            public static OverrideCompatibilityInfo receiverPresenceMismatch() {
482                return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "receiverPresenceMismatch"); // TODO
483            }
484    
485            @NotNull
486            public static OverrideCompatibilityInfo valueParameterNumberMismatch() {
487                return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "valueParameterNumberMismatch"); // TODO
488            }
489    
490            @NotNull
491            public static OverrideCompatibilityInfo boundsMismatch(TypeParameterDescriptor superTypeParameter, TypeParameterDescriptor subTypeParameter) {
492                return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "boundsMismatch"); // TODO
493            }
494    
495            @NotNull
496            public static OverrideCompatibilityInfo valueParameterTypeMismatch(JetType superValueParameter, JetType subValueParameter, Result result) {
497                return new OverrideCompatibilityInfo(result, "valueParameterTypeMismatch"); // TODO
498            }
499    
500            @NotNull
501            public static OverrideCompatibilityInfo memberKindMismatch() {
502                return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "memberKindMismatch"); // TODO
503            }
504    
505            @NotNull
506            public static OverrideCompatibilityInfo returnTypeMismatch(JetType substitutedSuperReturnType, JetType unsubstitutedSubReturnType) {
507                return new OverrideCompatibilityInfo(Result.CONFLICT, "returnTypeMismatch: " + unsubstitutedSubReturnType + " >< " + substitutedSuperReturnType); // TODO
508            }
509    
510            @NotNull
511            public static OverrideCompatibilityInfo varOverriddenByVal() {
512                return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "varOverriddenByVal"); // TODO
513            }
514    
515            @NotNull
516            public static OverrideCompatibilityInfo externalConditionFailed(Class<? extends ExternalOverridabilityCondition> conditionClass) {
517                return new OverrideCompatibilityInfo(Result.INCOMPATIBLE, "externalConditionFailed: " + conditionClass.getName()); // TODO
518            }
519    
520            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
521    
522            private final Result overridable;
523            private final String message;
524    
525            public OverrideCompatibilityInfo(Result success, String message) {
526                this.overridable = success;
527                this.message = message;
528            }
529    
530            public Result getResult() {
531                return overridable;
532            }
533    
534            public String getMessage() {
535                return message;
536            }
537        }
538    }