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(@NotNull 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        public static final Visibility PRIVATE_TO_THIS = new Visibility("private_to_this", false) {
074            @Override
075            public boolean isVisible(@NotNull ReceiverValue thisObject, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
076                if (PRIVATE.isVisible(thisObject, what, from)) {
077                    DeclarationDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class);
078    
079                    if (classDescriptor != null && thisObject instanceof ThisClassReceiver) {
080                        return ((ThisClassReceiver) thisObject).getClassDescriptor().getOriginal().equals(classDescriptor.getOriginal());
081                    }
082                }
083                return false;
084            }
085    
086            @Override
087            public boolean mustCheckInImports() {
088                return true;
089            }
090    
091            @NotNull
092            @Override
093            public String getDisplayName() {
094                return "private/*private to this*/";
095            }
096        };
097    
098        public static final Visibility PROTECTED = new Visibility("protected", true) {
099            @Override
100            public boolean mustCheckInImports() {
101                return false;
102            }
103    
104            @Override
105            public boolean isVisible(@NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
106                ClassDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class);
107                if (DescriptorUtils.isCompanionObject(classDescriptor)) {
108                    classDescriptor = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class);
109                }
110                if (classDescriptor == null) return false;
111    
112                ClassDescriptor fromClass = DescriptorUtils.getParentOfType(from, ClassDescriptor.class, false);
113                if (fromClass == null) return false;
114                if (DescriptorUtils.isSubclass(fromClass, classDescriptor)) {
115                    return true;
116                }
117                return isVisible(receiver, what, fromClass.getContainingDeclaration());
118            }
119        };
120    
121        public static final Visibility INTERNAL = new Visibility("internal", false) {
122            @Override
123            public boolean mustCheckInImports() {
124                return true;
125            }
126    
127            @Override
128            public boolean isVisible(@NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
129                DeclarationDescriptor fromOrModule = from instanceof PackageViewDescriptor ? ((PackageViewDescriptor) from).getModule() : from;
130                if (!DescriptorUtils.getContainingModule(what).isFriend(DescriptorUtils.getContainingModule(fromOrModule))) return false;
131    
132                return MODULE_VISIBILITY_HELPER.isInFriendModule(what, from);
133            }
134        };
135    
136        public static final Visibility PUBLIC = new Visibility("public", true) {
137            @Override
138            public boolean mustCheckInImports() {
139                return false;
140            }
141    
142            @Override
143            public boolean isVisible(@NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
144                return true;
145            }
146        };
147    
148        public static final Visibility LOCAL = new Visibility("local", false) {
149            @Override
150            public boolean mustCheckInImports() {
151                throw new IllegalStateException("This method shouldn't be invoked for LOCAL visibility");
152            }
153    
154            @Override
155            public boolean isVisible(@NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
156                throw new IllegalStateException("This method shouldn't be invoked for LOCAL visibility");
157            }
158        };
159    
160        public static final Visibility INHERITED = new Visibility("inherited", false) {
161            @Override
162            public boolean mustCheckInImports() {
163                throw new IllegalStateException("This method shouldn't be invoked for INHERITED visibility");
164            }
165    
166            @Override
167            public boolean isVisible(@NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
168                throw new IllegalStateException("Visibility is unknown yet"); //This method shouldn't be invoked for INHERITED visibility
169            }
170        };
171    
172        /* Visibility for fake override invisible members (they are created for better error reporting) */
173        public static final Visibility INVISIBLE_FAKE = new Visibility("invisible_fake", false) {
174            @Override
175            public boolean mustCheckInImports() {
176                throw new IllegalStateException("This method shouldn't be invoked for INVISIBLE_FAKE visibility");
177            }
178    
179            @Override
180            public boolean isVisible(@NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
181                return false;
182            }
183        };
184    
185        // Currently used as default visibility of FunctionDescriptor
186        // It's needed to prevent NPE when requesting non-nullable visibility of descriptor before `initialize` has been called
187        public static final Visibility UNKNOWN = new Visibility("unknown", false) {
188            @Override
189            public boolean mustCheckInImports() {
190                throw new IllegalStateException("This method shouldn't be invoked for UNKNOWN visibility");
191            }
192    
193            @Override
194            public boolean isVisible(
195                    @NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from
196            ) {
197                return false;
198            }
199        };
200    
201        public static final Set<Visibility> INVISIBLE_FROM_OTHER_MODULES =
202                Collections.unmodifiableSet(SetsKt.setOf(PRIVATE, PRIVATE_TO_THIS, INTERNAL, LOCAL));
203    
204        private Visibilities() {
205        }
206    
207        public static boolean isVisible(@NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
208            return findInvisibleMember(receiver, what, from) == null;
209        }
210    
211        @Nullable
212        public static DeclarationDescriptorWithVisibility findInvisibleMember(
213                @NotNull ReceiverValue receiver,
214                @NotNull DeclarationDescriptorWithVisibility what,
215                @NotNull DeclarationDescriptor from
216        ) {
217            DeclarationDescriptorWithVisibility parent = (DeclarationDescriptorWithVisibility) what.getOriginal();
218            while (parent != null && parent.getVisibility() != LOCAL) {
219                if (!parent.getVisibility().isVisible(receiver, parent, from)) {
220                    return parent;
221                }
222                parent = DescriptorUtils.getParentOfType(parent, DeclarationDescriptorWithVisibility.class);
223            }
224            return null;
225        }
226    
227        private static final Map<Visibility, Integer> ORDERED_VISIBILITIES;
228    
229        static {
230            Map<Visibility, Integer> visibilities = CollectionsKt.newHashMapWithExpectedSize(4);
231            visibilities.put(PRIVATE_TO_THIS, 0);
232            visibilities.put(PRIVATE, 0);
233            visibilities.put(INTERNAL, 1);
234            visibilities.put(PROTECTED, 1);
235            visibilities.put(PUBLIC, 2);
236            ORDERED_VISIBILITIES = Collections.unmodifiableMap(visibilities);
237        }
238    
239        /*package*/
240        @Nullable
241        static Integer compareLocal(@NotNull Visibility first, @NotNull Visibility second) {
242            if (first == second) return 0;
243            Integer firstIndex = ORDERED_VISIBILITIES.get(first);
244            Integer secondIndex = ORDERED_VISIBILITIES.get(second);
245            if (firstIndex == null || secondIndex == null || firstIndex.equals(secondIndex)) {
246                return null;
247            }
248            return firstIndex - secondIndex;
249        }
250    
251        @Nullable
252        public static Integer compare(@NotNull Visibility first, @NotNull Visibility second) {
253            Integer result = first.compareTo(second);
254            if (result != null) {
255                return result;
256            }
257            Integer oppositeResult = second.compareTo(first);
258            if (oppositeResult != null) {
259                return -oppositeResult;
260            }
261            return null;
262        }
263    
264        public static final Visibility DEFAULT_VISIBILITY = PUBLIC;
265    
266        public static boolean isPrivate(@NotNull Visibility visibility) {
267            return visibility == PRIVATE || visibility == PRIVATE_TO_THIS;
268        }
269    
270        @NotNull
271        private static final ModuleVisibilityHelper MODULE_VISIBILITY_HELPER;
272    
273        static {
274            Iterator<ModuleVisibilityHelper> iterator = ServiceLoader.load(ModuleVisibilityHelper.class, ModuleVisibilityHelper.class.getClassLoader()).iterator();
275            MODULE_VISIBILITY_HELPER = iterator.hasNext() ? iterator.next() : ModuleVisibilityHelper.EMPTY.INSTANCE$;
276        }
277    }