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