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