001    /*
002     * Copyright 2010-2015 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.kotlin.descriptors;
018    
019    import kotlin.SetsKt;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.resolve.DescriptorUtils;
023    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
024    import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver;
025    import org.jetbrains.kotlin.util.ModuleVisibilityHelper;
026    import org.jetbrains.kotlin.utils.CollectionsKt;
027    
028    import java.util.*;
029    
030    public class Visibilities {
031        public static final Visibility PRIVATE = new Visibility("private", false) {
032            @Override
033            public boolean mustCheckInImports() {
034                return true;
035            }
036    
037            @Override
038            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
039                if (DescriptorUtils.isTopLevelDeclaration(what)) {
040                    SourceFile fromContainingFile = DescriptorUtils.getContainingSourceFile(from);
041                    if (fromContainingFile != SourceFile.NO_SOURCE_FILE) {
042                        return fromContainingFile.equals(DescriptorUtils.getContainingSourceFile(what));
043                    }
044                }
045    
046                DeclarationDescriptor parent = what;
047                while (parent != null) {
048                    parent = parent.getContainingDeclaration();
049                    if ((parent instanceof ClassDescriptor && !DescriptorUtils.isCompanionObject(parent)) ||
050                        parent instanceof PackageFragmentDescriptor) {
051                        break;
052                    }
053                }
054                if (parent == null) {
055                    return false;
056                }
057                DeclarationDescriptor fromParent = from;
058                while (fromParent != null) {
059                    if (parent == fromParent) {
060                        return true;
061                    }
062                    if (fromParent instanceof PackageFragmentDescriptor) {
063                        return parent instanceof PackageFragmentDescriptor
064                               && ((PackageFragmentDescriptor) parent).getFqName().equals(((PackageFragmentDescriptor) fromParent).getFqName())
065                               && DescriptorUtils.areInSameModule(fromParent, parent);
066                    }
067                    fromParent = fromParent.getContainingDeclaration();
068                }
069                return false;
070            }
071        };
072    
073        /**
074         * This visibility is needed for the next case:
075         *  class A<in T>(t: T) {
076         *      private val t: T = t // visibility for t is PRIVATE_TO_THIS
077         *
078         *      fun test() {
079         *          val x: T = t // correct
080         *          val y: T = this.t // also correct
081         *      }
082         *      fun foo(a: A<String>) {
083         *         val x: String = a.t // incorrect, because a.t can be Any
084         *      }
085         *  }
086         */
087        public static final Visibility PRIVATE_TO_THIS = new Visibility("private_to_this", false) {
088            @Override
089            public boolean isVisible(@Nullable ReceiverValue thisObject, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
090                if (PRIVATE.isVisible(thisObject, what, from)) {
091                    DeclarationDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class);
092    
093                    if (classDescriptor != null && thisObject instanceof ThisClassReceiver) {
094                        return ((ThisClassReceiver) thisObject).getClassDescriptor().getOriginal().equals(classDescriptor.getOriginal());
095                    }
096                }
097                return false;
098            }
099    
100            @Override
101            public boolean mustCheckInImports() {
102                return true;
103            }
104    
105            @NotNull
106            @Override
107            public String getDisplayName() {
108                return "private/*private to this*/";
109            }
110        };
111    
112        public static final Visibility PROTECTED = new Visibility("protected", true) {
113            @Override
114            public boolean mustCheckInImports() {
115                return false;
116            }
117    
118            @Override
119            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
120                ClassDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class);
121                if (DescriptorUtils.isCompanionObject(classDescriptor)) {
122                    classDescriptor = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class);
123                }
124                if (classDescriptor == null) return false;
125    
126                ClassDescriptor fromClass = DescriptorUtils.getParentOfType(from, ClassDescriptor.class, false);
127                if (fromClass == null) return false;
128                if (DescriptorUtils.isSubclass(fromClass, classDescriptor)) {
129                    return true;
130                }
131                return isVisible(receiver, what, fromClass.getContainingDeclaration());
132            }
133        };
134    
135        public static final Visibility INTERNAL = new Visibility("internal", false) {
136            @Override
137            public boolean mustCheckInImports() {
138                return true;
139            }
140    
141            @Override
142            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
143                DeclarationDescriptor fromOrModule = from instanceof PackageViewDescriptor ? ((PackageViewDescriptor) from).getModule() : from;
144                if (!DescriptorUtils.getContainingModule(what).isFriend(DescriptorUtils.getContainingModule(fromOrModule))) return false;
145    
146                return MODULE_VISIBILITY_HELPER.isInFriendModule(what, from);
147            }
148        };
149    
150        public static final Visibility PUBLIC = new Visibility("public", true) {
151            @Override
152            public boolean mustCheckInImports() {
153                return false;
154            }
155    
156            @Override
157            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
158                return true;
159            }
160        };
161    
162        public static final Visibility LOCAL = new Visibility("local", false) {
163            @Override
164            public boolean mustCheckInImports() {
165                throw new IllegalStateException("This method shouldn't be invoked for LOCAL visibility");
166            }
167    
168            @Override
169            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
170                throw new IllegalStateException("This method shouldn't be invoked for LOCAL visibility");
171            }
172        };
173    
174        public static final Visibility INHERITED = new Visibility("inherited", false) {
175            @Override
176            public boolean mustCheckInImports() {
177                throw new IllegalStateException("This method shouldn't be invoked for INHERITED visibility");
178            }
179    
180            @Override
181            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
182                throw new IllegalStateException("Visibility is unknown yet"); //This method shouldn't be invoked for INHERITED visibility
183            }
184        };
185    
186        /* Visibility for fake override invisible members (they are created for better error reporting) */
187        public static final Visibility INVISIBLE_FAKE = new Visibility("invisible_fake", false) {
188            @Override
189            public boolean mustCheckInImports() {
190                throw new IllegalStateException("This method shouldn't be invoked for INVISIBLE_FAKE visibility");
191            }
192    
193            @Override
194            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
195                return false;
196            }
197        };
198    
199        // Currently used as default visibility of FunctionDescriptor
200        // It's needed to prevent NPE when requesting non-nullable visibility of descriptor before `initialize` has been called
201        public static final Visibility UNKNOWN = new Visibility("unknown", false) {
202            @Override
203            public boolean mustCheckInImports() {
204                throw new IllegalStateException("This method shouldn't be invoked for UNKNOWN visibility");
205            }
206    
207            @Override
208            public boolean isVisible(
209                    @Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from
210            ) {
211                return false;
212            }
213        };
214    
215        public static final Set<Visibility> INVISIBLE_FROM_OTHER_MODULES =
216                Collections.unmodifiableSet(SetsKt.setOf(PRIVATE, PRIVATE_TO_THIS, INTERNAL, LOCAL));
217    
218        private Visibilities() {
219        }
220    
221        public static boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
222            return findInvisibleMember(receiver, what, from) == null;
223        }
224    
225        /**
226         * Receiver used only for visibility PRIVATE_TO_THIS.
227         * For all other visibilities this method give correct result.
228         */
229        public static boolean isVisibleWithIrrelevantReceiver(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
230            return findInvisibleMember(null, what, from) == null;
231        }
232    
233        @Nullable
234        public static DeclarationDescriptorWithVisibility findInvisibleMember(
235                @Nullable ReceiverValue receiver,
236                @NotNull DeclarationDescriptorWithVisibility what,
237                @NotNull DeclarationDescriptor from
238        ) {
239            DeclarationDescriptorWithVisibility parent = (DeclarationDescriptorWithVisibility) what.getOriginal();
240            while (parent != null && parent.getVisibility() != LOCAL) {
241                if (!parent.getVisibility().isVisible(receiver, parent, from)) {
242                    return parent;
243                }
244                parent = DescriptorUtils.getParentOfType(parent, DeclarationDescriptorWithVisibility.class);
245            }
246            return null;
247        }
248    
249        private static final Map<Visibility, Integer> ORDERED_VISIBILITIES;
250    
251        static {
252            Map<Visibility, Integer> visibilities = CollectionsKt.newHashMapWithExpectedSize(4);
253            visibilities.put(PRIVATE_TO_THIS, 0);
254            visibilities.put(PRIVATE, 0);
255            visibilities.put(INTERNAL, 1);
256            visibilities.put(PROTECTED, 1);
257            visibilities.put(PUBLIC, 2);
258            ORDERED_VISIBILITIES = Collections.unmodifiableMap(visibilities);
259        }
260    
261        /*package*/
262        @Nullable
263        static Integer compareLocal(@NotNull Visibility first, @NotNull Visibility second) {
264            if (first == second) return 0;
265            Integer firstIndex = ORDERED_VISIBILITIES.get(first);
266            Integer secondIndex = ORDERED_VISIBILITIES.get(second);
267            if (firstIndex == null || secondIndex == null || firstIndex.equals(secondIndex)) {
268                return null;
269            }
270            return firstIndex - secondIndex;
271        }
272    
273        @Nullable
274        public static Integer compare(@NotNull Visibility first, @NotNull Visibility second) {
275            Integer result = first.compareTo(second);
276            if (result != null) {
277                return result;
278            }
279            Integer oppositeResult = second.compareTo(first);
280            if (oppositeResult != null) {
281                return -oppositeResult;
282            }
283            return null;
284        }
285    
286        public static final Visibility DEFAULT_VISIBILITY = PUBLIC;
287    
288        public static boolean isPrivate(@NotNull Visibility visibility) {
289            return visibility == PRIVATE || visibility == PRIVATE_TO_THIS;
290        }
291    
292        @NotNull
293        private static final ModuleVisibilityHelper MODULE_VISIBILITY_HELPER;
294    
295        static {
296            Iterator<ModuleVisibilityHelper> iterator = ServiceLoader.load(ModuleVisibilityHelper.class, ModuleVisibilityHelper.class.getClassLoader()).iterator();
297            MODULE_VISIBILITY_HELPER = iterator.hasNext() ? iterator.next() : ModuleVisibilityHelper.EMPTY.INSTANCE$;
298        }
299    }