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