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
017package org.jetbrains.jet.lang.resolve.java.wrapper;
018
019import com.google.common.collect.Lists;
020import com.intellij.openapi.util.Pair;
021import com.intellij.psi.PsiClass;
022import com.intellij.psi.PsiMember;
023import org.jetbrains.annotations.NotNull;
024import org.jetbrains.annotations.Nullable;
025import org.jetbrains.jet.lang.resolve.java.JvmAbi;
026import org.jetbrains.jet.lang.resolve.java.TypeSource;
027
028import java.util.Collection;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032
033
034/*
035* Data from PSI related to one property used to resolve this property.
036*/
037public 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}