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 }