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;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.openapi.util.text.StringUtil;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.asm4.Type;
023    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
024    import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
025    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
026    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027    import org.jetbrains.jet.lang.resolve.name.FqName;
028    
029    import java.util.List;
030    
031    public class JvmClassName {
032    
033        @NotNull
034        public static JvmClassName byInternalName(@NotNull String internalName) {
035            return new JvmClassName(internalName);
036        }
037    
038        @NotNull
039        public static JvmClassName byType(@NotNull Type type) {
040            if (type.getSort() != Type.OBJECT) {
041                throw new IllegalArgumentException("Type is not convertible to " + JvmClassName.class.getSimpleName() + ": " + type);
042            }
043            return byInternalName(type.getInternalName());
044        }
045    
046        /**
047         * WARNING: fq name cannot be uniquely mapped to JVM class name.
048         */
049        @NotNull
050        public static JvmClassName byFqNameWithoutInnerClasses(@NotNull FqName fqName) {
051            JvmClassName r = new JvmClassName(fqNameToInternalName(fqName));
052            r.fqName = fqName;
053            return r;
054        }
055    
056        @NotNull
057        public static JvmClassName byFqNameWithoutInnerClasses(@NotNull String fqName) {
058            return byFqNameWithoutInnerClasses(new FqName(fqName));
059        }
060    
061        @NotNull
062        public static JvmClassName bySignatureName(@NotNull String signatureName) {
063            JvmClassName className = new JvmClassName(signatureNameToInternalName(signatureName));
064            className.signatureName = signatureName;
065            return className;
066        }
067    
068        private static String encodeSpecialNames(String str) {
069            String encodedObjectNames = StringUtil.replace(str, JvmAbi.CLASS_OBJECT_CLASS_NAME, CLASS_OBJECT_REPLACE_GUARD);
070            return StringUtil.replace(encodedObjectNames, JvmAbi.TRAIT_IMPL_CLASS_NAME, TRAIT_IMPL_REPLACE_GUARD);
071        }
072    
073        private static String decodeSpecialNames(String str) {
074            String decodedObjectNames = StringUtil.replace(str, CLASS_OBJECT_REPLACE_GUARD, JvmAbi.CLASS_OBJECT_CLASS_NAME);
075            return StringUtil.replace(decodedObjectNames, TRAIT_IMPL_REPLACE_GUARD, JvmAbi.TRAIT_IMPL_CLASS_NAME);
076        }
077    
078        @NotNull
079        private static JvmClassName byFqNameAndInnerClassList(@NotNull FqName fqName, @NotNull List<String> innerClassList) {
080            String outerClassName = fqNameToInternalName(fqName);
081            StringBuilder sb = new StringBuilder(outerClassName);
082            for (String innerClassName : innerClassList) {
083                sb.append("$").append(innerClassName);
084            }
085            return new JvmClassName(sb.toString());
086        }
087    
088        @NotNull
089        public static JvmClassName byClassDescriptor(@NotNull ClassifierDescriptor classDescriptor) {
090            DeclarationDescriptor descriptor = classDescriptor;
091    
092            List<String> innerClassNames = Lists.newArrayList();
093            while (descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
094                innerClassNames.add(descriptor.getName().asString());
095                descriptor = descriptor.getContainingDeclaration();
096                assert descriptor != null;
097            }
098    
099            return byFqNameAndInnerClassList(DescriptorUtils.getFQName(descriptor).toSafe(), innerClassNames);
100        }
101    
102        @NotNull
103        private static String fqNameToInternalName(@NotNull FqName fqName) {
104            return fqName.asString().replace('.', '/');
105        }
106    
107        @NotNull
108        private static String signatureNameToInternalName(@NotNull String signatureName) {
109            return signatureName.replace('.', '$');
110        }
111    
112        @NotNull
113        private static String internalNameToFqName(@NotNull String name) {
114            return decodeSpecialNames(encodeSpecialNames(name).replace('$', '.').replace('/', '.'));
115        }
116    
117        @NotNull
118        private static String internalNameToSignatureName(@NotNull String name) {
119            return decodeSpecialNames(encodeSpecialNames(name).replace('$', '.'));
120        }
121    
122        @NotNull
123        private static String signatureNameToFqName(@NotNull String name) {
124            return name.replace('/', '.');
125        }
126    
127    
128        private final static String CLASS_OBJECT_REPLACE_GUARD = "<class_object>";
129        private final static String TRAIT_IMPL_REPLACE_GUARD = "<trait_impl>";
130    
131        // Internal name:  jet/Map$Entry
132        // FqName:         jet.Map.Entry
133        // Signature name: jet/Map.Entry
134    
135        private final String internalName;
136        private FqName fqName;
137        private String descriptor;
138        private String signatureName;
139    
140        private Type asmType;
141    
142        private JvmClassName(@NotNull String internalName) {
143            this.internalName = internalName;
144        }
145    
146        @NotNull
147        public FqName getFqName() {
148            if (fqName == null) {
149                this.fqName = new FqName(internalNameToFqName(internalName));
150            }
151            return fqName;
152        }
153    
154        @NotNull
155        public String getInternalName() {
156            return internalName;
157        }
158    
159        @NotNull
160        public String getDescriptor() {
161            if (descriptor == null) {
162                StringBuilder sb = new StringBuilder(internalName.length() + 2);
163                sb.append('L');
164                sb.append(internalName);
165                sb.append(';');
166                descriptor = sb.toString();
167            }
168            return descriptor;
169        }
170    
171        @NotNull
172        public Type getAsmType() {
173            if (asmType == null) {
174                asmType = Type.getType(getDescriptor());
175            }
176            return asmType;
177        }
178    
179        @NotNull
180        public String getSignatureName() {
181            if (signatureName == null) {
182                signatureName = internalNameToSignatureName(internalName);
183            }
184            return signatureName;
185        }
186    
187        @NotNull
188        public FqName getOuterClassFqName() {
189            String signatureName = getSignatureName();
190            int index = signatureName.indexOf('.');
191            String outerClassName = index != -1 ? signatureName.substring(0, index) : signatureName;
192            return new FqName(signatureNameToFqName(outerClassName));
193        }
194    
195        @NotNull
196        public List<String> getInnerClassNameList() {
197            List<String> innerClassList = Lists.newArrayList();
198            String signatureName = getSignatureName();
199            int index = signatureName.indexOf('.');
200            while (index != -1) {
201                int nextIndex = signatureName.indexOf('.', index + 1);
202                String innerClassName = nextIndex != -1 ? signatureName.substring(index + 1, nextIndex) : signatureName.substring(index + 1);
203                innerClassList.add(innerClassName);
204                index = nextIndex;
205            }
206            return innerClassList;
207        }
208    
209        @Override
210        public String toString() {
211            return getInternalName();
212        }
213    
214        @Override
215        public boolean equals(Object o) {
216            // generated by Idea
217            if (this == o) return true;
218            if (o == null || getClass() != o.getClass()) return false;
219    
220            JvmClassName name = (JvmClassName) o;
221    
222            if (!internalName.equals(name.internalName)) return false;
223    
224            return true;
225        }
226    
227        @Override
228        public int hashCode() {
229            // generated by Idea
230            return internalName.hashCode();
231        }
232    }