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