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.resolve.java.wrapper;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.openapi.util.Pair;
021    import com.intellij.psi.PsiClass;
022    import com.intellij.psi.PsiMember;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
026    import org.jetbrains.jet.lang.resolve.java.TypeSource;
027    
028    import java.util.Collection;
029    import java.util.HashMap;
030    import java.util.List;
031    import java.util.Map;
032    
033    
034    /*
035    * Data from PSI related to one property used to resolve this property.
036    */
037    public final class PropertyPsiData {
038    
039        @NotNull
040        public static Collection<PropertyPsiData> assemblePropertyPsiDataFromElements(@NotNull List<PropertyPsiDataElement> elements) {
041            Map<String, PropertyPsiData> map = new HashMap<String, PropertyPsiData>();
042            for (PropertyPsiDataElement element : elements) {
043                String key = propertyKeyForGrouping(element);
044    
045                PropertyPsiData value = map.get(key);
046                if (value == null) {
047                    value = new PropertyPsiData();
048                    map.put(key, value);
049                }
050    
051                if (element.isGetter()) {
052                    checkDuplicatePropertyComponent(element, "getter", value.getter);
053                    value.getter = element;
054                }
055                else if (element.isSetter()) {
056                    checkDuplicatePropertyComponent(element, "setter", value.setter);
057                    value.setter = element;
058                }
059                else if (element.isField()) {
060                    checkDuplicatePropertyComponent(element, "field", value.field);
061                    value.field = element;
062                }
063                else {
064                    throw new IllegalStateException();
065                }
066            }
067    
068            return map.values();
069        }
070    
071        private static void checkDuplicatePropertyComponent(
072                @NotNull PropertyPsiDataElement checked,  @NotNull String componentTypeName, @Nullable PropertyPsiDataElement existent) {
073            if (existent != null) {
074                PsiClass checkedElementClass = checked.getMember().getPsiMember().getContainingClass();
075                PsiClass existentElementClass = existent.getMember().getPsiMember().getContainingClass();
076    
077                throw new IllegalStateException(
078                        String.format("Psi element '%s' in class '%s' overwrites '%s' in class '%s' while generating %s component for property",
079                                      checked.getMember().getPsiMember(), checkedElementClass != null ? checkedElementClass.getQualifiedName() : "<no-class>",
080                                      existent.getMember().getPsiMember(), existentElementClass != null ? existentElementClass.getQualifiedName() : "<no-class>",
081                                      componentTypeName));
082            }
083        }
084    
085        @NotNull
086        private static String propertyKeyForGrouping(@NotNull PropertyPsiDataElement propertyAccessor) {
087            String type = key(propertyAccessor.getType());
088            String receiverType = key(propertyAccessor.getReceiverType());
089            return Pair.create(type, receiverType).toString();
090        }
091    
092        @NotNull
093        private static String key(@Nullable TypeSource typeSource) {
094            if (typeSource == null) {
095                return "";
096            }
097            else if (typeSource.getTypeString().length() > 0) {
098                return typeSource.getTypeString();
099            }
100            else {
101                return typeSource.getPsiType().getPresentableText();
102            }
103        }
104    
105        @Nullable
106        private PropertyPsiDataElement getter = null;
107        @Nullable
108        private PropertyPsiDataElement setter = null;
109        @Nullable
110        private PropertyPsiDataElement field = null;
111        @Nullable
112        private Collection<PropertyPsiDataElement> elements = null;
113    
114        @Nullable
115        public PropertyPsiDataElement getGetter() {
116            return getter;
117        }
118    
119        @Nullable
120        public PropertyPsiDataElement getSetter() {
121            return setter;
122        }
123    
124        @SuppressWarnings("ConstantConditions")
125        @NotNull
126        private Collection<PropertyPsiDataElement> getElements() {
127            if (elements == null) {
128                elements = Lists.newArrayList();
129                if (getter != null) {
130                    elements.add(getter);
131                }
132                if (setter != null) {
133                    elements.add(setter);
134                }
135                if (field != null) {
136                    elements.add(field);
137                }
138                assert !elements.isEmpty();
139            }
140            return elements;
141        }
142    
143        public boolean isExtension() {
144            boolean isExtension = getCharacteristicMember().isExtension();
145            for (PropertyPsiDataElement element : getElements()) {
146                assert (element.isExtension() == isExtension);
147            }
148            return isExtension;
149        }
150    
151        public boolean isStatic() {
152            boolean isStatic = getCharacteristicMember().getMember().isStatic();
153            for (PropertyPsiDataElement element : getElements()) {
154                assert (element.getMember().isStatic() == isStatic);
155            }
156            return isStatic;
157        }
158    
159        @NotNull
160        public PropertyPsiDataElement getCharacteristicMember() {
161            if (getter != null) {
162                return getter;
163            }
164            if (field != null) {
165                return field;
166            }
167            if (setter != null) {
168                return setter;
169            }
170            throw new IllegalStateException();
171        }
172    
173        @NotNull
174        public PsiMember getCharacteristicPsi() {
175            return getCharacteristicMember().getMember().getPsiMember();
176        }
177    
178        public boolean isVar() {
179            if (getter == null && setter == null) {
180                assert field != null;
181                return !field.getMember().isFinal();
182            }
183            return setter != null;
184        }
185    
186        public boolean isStaticFinalField() {
187            if (getter != null || setter != null) {
188                return false;
189            }
190            assert field != null;
191            return field.getMember().isFinal() && field.getMember().isStatic();
192        }
193    
194        public boolean isPropertyForNamedObject() {
195            return field != null && JvmAbi.INSTANCE_FIELD.equals(field.getMember().getName());
196        }
197    
198        public boolean isFinal() {
199            if (getter != null) {
200                return getter.getMember().isFinal();
201            }
202    
203            if (setter != null) {
204                return setter.getMember().isFinal();
205            }
206            return false;
207        }
208    }