001    /*
002     * Copyright 2010-2013 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.jet.lang.descriptors;
018    
019    import kotlin.KotlinPackage;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
023    import org.jetbrains.jet.utils.UtilsPackage;
024    
025    import java.util.Collections;
026    import java.util.Map;
027    import java.util.Set;
028    
029    public class Visibilities {
030        public static final Visibility PRIVATE = new Visibility("private", false) {
031            @Override
032            protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
033                DeclarationDescriptor parent = what;
034                while (parent != null) {
035                    parent = parent.getContainingDeclaration();
036                    if ((parent instanceof ClassDescriptor && !DescriptorUtils.isClassObject(parent)) ||
037                        parent instanceof PackageFragmentDescriptor) {
038                        break;
039                    }
040                }
041                if (parent == null) {
042                    return false;
043                }
044                DeclarationDescriptor fromParent = from;
045                while (fromParent != null) {
046                    if (parent == fromParent) {
047                        return true;
048                    }
049                    if (fromParent instanceof PackageFragmentDescriptor) {
050                        return parent instanceof PackageFragmentDescriptor
051                               && ((PackageFragmentDescriptor) parent).getFqName().isAncestorOf(((PackageFragmentDescriptor) fromParent).getFqName())
052                               && DescriptorUtils.areInSameModule(fromParent, parent);
053                    }
054                    fromParent = fromParent.getContainingDeclaration();
055                }
056                return false;
057            }
058        };
059    
060        public static final Visibility PROTECTED = new Visibility("protected", true) {
061            @Override
062            protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
063                ClassDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class);
064                if (classDescriptor == null) return false;
065    
066                ClassDescriptor fromClass = DescriptorUtils.getParentOfType(from, ClassDescriptor.class, false);
067                if (fromClass == null) return false;
068                if (DescriptorUtils.isSubclass(fromClass, classDescriptor)) {
069                    return true;
070                }
071                return isVisible(what, fromClass.getContainingDeclaration());
072            }
073        };
074    
075        public static final Visibility INTERNAL = new Visibility("internal", false) {
076            @Override
077            protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
078                //NOTE: supposedly temporarily
079                return PUBLIC.isVisible(what, from);
080            }
081        };
082    
083        public static final Visibility PUBLIC = new Visibility("public", true) {
084            @Override
085            protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
086                return true;
087            }
088        };
089    
090        public static final Visibility LOCAL = new Visibility("local", false) {
091            @Override
092            protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
093                throw new IllegalStateException(); //This method shouldn't be invoked for LOCAL visibility
094            }
095        };
096    
097        public static final Visibility INHERITED = new Visibility("inherited", false) {
098            @Override
099            protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
100                throw new IllegalStateException("Visibility is unknown yet"); //This method shouldn't be invoked for INHERITED visibility
101            }
102        };
103    
104        /* Visibility for fake override invisible members (they are created for better error reporting) */
105        public static final Visibility INVISIBLE_FAKE = new Visibility("invisible_fake", false) {
106            @Override
107            protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
108                return false;
109            }
110        };
111    
112        public static final Set<Visibility> INVISIBLE_FROM_OTHER_MODULES =
113                Collections.unmodifiableSet(KotlinPackage.setOf(PRIVATE, INTERNAL, LOCAL));
114    
115        private Visibilities() {
116        }
117    
118        public static boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
119            return findInvisibleMember(what, from) == null;
120        }
121    
122        @SuppressWarnings("UnusedDeclaration")
123        private static boolean isInFriendModule(@NotNull DeclarationDescriptor what, @NotNull DeclarationDescriptor from) {
124            return DescriptorUtils.getContainingModule(what).isFriend(DescriptorUtils.getContainingModule(from));
125        }
126    
127        @Nullable
128        public static DeclarationDescriptorWithVisibility findInvisibleMember(
129                @NotNull DeclarationDescriptorWithVisibility what,
130                @NotNull DeclarationDescriptor from
131        ) {
132            DeclarationDescriptorWithVisibility parent = what;
133            while (parent != null && parent.getVisibility() != LOCAL) {
134                if (!parent.getVisibility().isVisible(parent, from)) {
135                    return parent;
136                }
137                parent = DescriptorUtils.getParentOfType(parent, DeclarationDescriptorWithVisibility.class);
138            }
139            return null;
140        }
141    
142        private static final Map<Visibility, Integer> ORDERED_VISIBILITIES;
143    
144        static {
145            Map<Visibility, Integer> visibilities = UtilsPackage.newHashMapWithExpectedSize(4);
146            visibilities.put(PRIVATE, 0);
147            visibilities.put(INTERNAL, 1);
148            visibilities.put(PROTECTED, 1);
149            visibilities.put(PUBLIC, 2);
150            ORDERED_VISIBILITIES = Collections.unmodifiableMap(visibilities);
151        }
152    
153        /*package*/
154        @Nullable
155        static Integer compareLocal(@NotNull Visibility first, @NotNull Visibility second) {
156            if (first == second) return 0;
157            Integer firstIndex = ORDERED_VISIBILITIES.get(first);
158            Integer secondIndex = ORDERED_VISIBILITIES.get(second);
159            if (firstIndex == null || secondIndex == null || firstIndex.equals(secondIndex)) {
160                return null;
161            }
162            return firstIndex - secondIndex;
163        }
164    
165        @Nullable
166        public static Integer compare(@NotNull Visibility first, @NotNull Visibility second) {
167            Integer result = first.compareTo(second);
168            if (result != null) {
169                return result;
170            }
171            Integer oppositeResult = second.compareTo(first);
172            if (oppositeResult != null) {
173                return -oppositeResult;
174            }
175            return null;
176        }
177    }