001    package org.jetbrains.jet.descriptors.serialization;
002    
003    import com.google.protobuf.Internal;
004    import org.jetbrains.annotations.NotNull;
005    import org.jetbrains.jet.lang.descriptors.*;
006    
007    public class Flags {
008        private Flags() {}
009    
010        // Common
011    
012        public static final FlagField<Boolean> HAS_ANNOTATIONS = FlagField.booleanFirst();
013    
014        public static final FlagField<ProtoBuf.Visibility> VISIBILITY = FlagField.after(HAS_ANNOTATIONS, ProtoBuf.Visibility.values());
015    
016        public static final FlagField<ProtoBuf.Modality> MODALITY = FlagField.after(VISIBILITY, ProtoBuf.Modality.values());
017    
018        // Class
019    
020        public static final FlagField<ProtoBuf.Class.Kind> CLASS_KIND = FlagField.after(MODALITY, ProtoBuf.Class.Kind.values());
021    
022        public static final FlagField<Boolean> INNER = FlagField.booleanAfter(CLASS_KIND);
023    
024        // Callables
025    
026        public static final FlagField<ProtoBuf.Callable.CallableKind> CALLABLE_KIND = FlagField.after(MODALITY,
027                                                                                                      ProtoBuf.Callable.CallableKind.values());
028    
029        public static final FlagField<ProtoBuf.Callable.MemberKind> MEMBER_KIND = FlagField.after(CALLABLE_KIND,
030                                                                                                  ProtoBuf.Callable.MemberKind.values());
031        public static final FlagField<Boolean> HAS_GETTER = FlagField.booleanAfter(MEMBER_KIND);
032        public static final FlagField<Boolean> HAS_SETTER = FlagField.booleanAfter(HAS_GETTER);
033    
034        // Parameters
035    
036        public static final FlagField<Boolean> DECLARES_DEFAULT_VALUE = FlagField.booleanAfter(HAS_ANNOTATIONS);
037    
038        // Accessors
039    
040        // It's important that this flag is negated: "is NOT default" instead of "is default"
041        public static final FlagField<Boolean> IS_NOT_DEFAULT = FlagField.booleanAfter(MODALITY);
042    
043        // ---
044    
045        private static <E> int bitWidth(@NotNull E[] enumEntries) {
046            int length = enumEntries.length - 1;
047            if (length == 0) return 1;
048            for (int i = 31; i >= 0; i--) {
049                if ((length & (1 << i)) != 0) return i + 1;
050            }
051            throw new IllegalStateException("Empty enum: " + enumEntries.getClass());
052        }
053    
054        public static int getClassFlags(
055                boolean hasAnnotations,
056                Visibility visibility,
057                Modality modality,
058                ClassKind kind,
059                boolean inner
060        ) {
061            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
062                   | MODALITY.toFlags(modality(modality))
063                   | VISIBILITY.toFlags(visibility(visibility))
064                   | CLASS_KIND.toFlags(classKind(kind))
065                   | INNER.toFlags(inner)
066                   ;
067        }
068    
069        private static ProtoBuf.Class.Kind classKind(ClassKind kind) {
070            switch (kind) {
071                case CLASS:
072                    return ProtoBuf.Class.Kind.CLASS;
073                case TRAIT:
074                    return ProtoBuf.Class.Kind.TRAIT;
075                case ENUM_CLASS:
076                    return ProtoBuf.Class.Kind.ENUM_CLASS;
077                case ENUM_ENTRY:
078                    return ProtoBuf.Class.Kind.ENUM_ENTRY;
079                case ANNOTATION_CLASS:
080                    return ProtoBuf.Class.Kind.ANNOTATION_CLASS;
081                case OBJECT:
082                    return ProtoBuf.Class.Kind.OBJECT;
083                case CLASS_OBJECT:
084                    return ProtoBuf.Class.Kind.CLASS_OBJECT;
085            }
086            throw new IllegalArgumentException("Unknown class kind: " + kind);
087        }
088    
089        public static int getCallableFlags(
090                boolean hasAnnotations,
091                @NotNull Visibility visibility,
092                @NotNull Modality modality,
093                @NotNull CallableMemberDescriptor.Kind memberKind,
094                @NotNull ProtoBuf.Callable.CallableKind callableKind,
095                boolean hasGetter,
096                boolean hasSetter
097        ) {
098            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
099                   | MODALITY.toFlags(modality(modality))
100                   | VISIBILITY.toFlags(visibility(visibility))
101                   | MEMBER_KIND.toFlags(memberKind(memberKind))
102                   | CALLABLE_KIND.toFlags(callableKind)
103                   | HAS_GETTER.toFlags(hasGetter)
104                   | HAS_SETTER.toFlags(hasSetter)
105                   ;
106        }
107    
108        public static int getAccessorFlags(
109                boolean hasAnnotations,
110                @NotNull Visibility visibility,
111                @NotNull Modality modality,
112                boolean isNotDefault
113        ) {
114            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
115                   | MODALITY.toFlags(modality(modality))
116                   | VISIBILITY.toFlags(visibility(visibility))
117                   | IS_NOT_DEFAULT.toFlags(isNotDefault)
118                   ;
119        }
120    
121        @NotNull
122        private static ProtoBuf.Visibility visibility(@NotNull Visibility visibility) {
123            if (visibility == Visibilities.INTERNAL) {
124                return ProtoBuf.Visibility.INTERNAL;
125            }
126            else if (visibility == Visibilities.PUBLIC) {
127                return ProtoBuf.Visibility.PUBLIC;
128            }
129            else if (visibility == Visibilities.PRIVATE) {
130                return ProtoBuf.Visibility.PRIVATE;
131            }
132            else if (visibility == Visibilities.PROTECTED) {
133                return ProtoBuf.Visibility.PROTECTED;
134            }
135            return ProtoBuf.Visibility.EXTRA;
136        }
137    
138        @NotNull
139        private static ProtoBuf.Modality modality(@NotNull Modality modality) {
140            switch (modality) {
141                case FINAL:
142                    return ProtoBuf.Modality.FINAL;
143                case OPEN:
144                    return ProtoBuf.Modality.OPEN;
145                case ABSTRACT:
146                    return ProtoBuf.Modality.ABSTRACT;
147            }
148            throw new IllegalArgumentException("Unknown modality: " + modality);
149        }
150    
151        @NotNull
152        private static ProtoBuf.Callable.MemberKind memberKind(@NotNull CallableMemberDescriptor.Kind kind) {
153            switch (kind) {
154                case DECLARATION:
155                    return ProtoBuf.Callable.MemberKind.DECLARATION;
156                case FAKE_OVERRIDE:
157                    return ProtoBuf.Callable.MemberKind.FAKE_OVERRIDE;
158                case DELEGATION:
159                    return ProtoBuf.Callable.MemberKind.DELEGATION;
160                case SYNTHESIZED:
161                    return ProtoBuf.Callable.MemberKind.SYNTHESIZED;
162            }
163            throw new IllegalArgumentException("Unknown member kind: " + kind);
164        }
165    
166        public static int getValueParameterFlags(boolean hasAnnotations, boolean declaresDefaultValue) {
167            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
168                   | DECLARES_DEFAULT_VALUE.toFlags(declaresDefaultValue)
169                   ;
170        }
171    
172        // Infrastructure
173    
174        public static abstract class FlagField<E> {
175            public static <E extends Internal.EnumLite> FlagField<E> after(FlagField<?> previousField, E[] values) {
176                int offset = previousField.offset + previousField.bitWidth;
177                return new EnumLiteFlagField<E>(offset, values);
178            }
179    
180            public static <E extends Internal.EnumLite> FlagField<E> first(E[] values) {
181                return new EnumLiteFlagField<E>(0, values);
182            }
183    
184            public static FlagField<Boolean> booleanFirst() {
185                return new BooleanFlagField(0);
186            }
187    
188            public static FlagField<Boolean> booleanAfter(FlagField<?> previousField) {
189                int offset = previousField.offset + previousField.bitWidth;
190                return new BooleanFlagField(offset);
191            }
192    
193            private final int offset;
194            private final int bitWidth;
195            private final E[] values;
196    
197            private FlagField(int offset, E[] values) {
198                this.offset = offset;
199                this.bitWidth = bitWidth(values);
200                this.values = values;
201            }
202    
203            public E get(int flags) {
204                int maskUnshifted = (1 << bitWidth) - 1;
205                int mask = maskUnshifted << offset;
206                int value = (flags & mask) >> offset;
207                return values[value];
208            }
209    
210            public int toFlags(E value) {
211                return getIntValue(value) << offset;
212            }
213    
214            protected abstract int getIntValue(E value);
215    
216        }
217    
218        private static class BooleanFlagField extends FlagField<Boolean> {
219            private static final Boolean[] BOOLEAN = { false, true };
220    
221            public BooleanFlagField(int offset) {
222                super(offset, BOOLEAN);
223            }
224    
225            @Override
226            protected int getIntValue(Boolean value) {
227                return value ? 1 : 0;
228            }
229        }
230    
231        private static class EnumLiteFlagField<E extends Internal.EnumLite> extends FlagField<E> {
232            public EnumLiteFlagField(int offset, E[] values) {
233                super(offset, values);
234            }
235    
236            @Override
237            protected int getIntValue(E value) {
238                return value.getNumber();
239            }
240        }
241    
242    }