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