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; 018 019import com.google.common.collect.Lists; 020import com.intellij.openapi.util.text.StringUtil; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.asm4.Type; 023import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 024import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; 025import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 026import org.jetbrains.jet.lang.resolve.DescriptorUtils; 027import org.jetbrains.jet.lang.resolve.name.FqName; 028 029import java.util.List; 030 031public 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}