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.asJava; 018 019 import com.intellij.openapi.application.ApplicationManager; 020 import com.intellij.openapi.diagnostic.Logger; 021 import com.intellij.openapi.progress.ProcessCanceledException; 022 import com.intellij.openapi.project.Project; 023 import com.intellij.openapi.util.Comparing; 024 import com.intellij.openapi.util.io.FileUtil; 025 import com.intellij.openapi.vfs.StandardFileSystems; 026 import com.intellij.openapi.vfs.VirtualFile; 027 import com.intellij.psi.*; 028 import com.intellij.psi.impl.java.stubs.PsiClassStub; 029 import com.intellij.psi.impl.light.LightTypeParameterListBuilder; 030 import com.intellij.psi.search.GlobalSearchScope; 031 import com.intellij.psi.stubs.PsiFileStub; 032 import com.intellij.psi.stubs.StubElement; 033 import com.intellij.psi.util.PsiTreeUtil; 034 import com.intellij.util.PathUtil; 035 import com.intellij.util.SmartList; 036 import org.jetbrains.annotations.NotNull; 037 import org.jetbrains.annotations.Nullable; 038 import org.jetbrains.jet.lang.psi.*; 039 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 040 import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; 041 import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod; 042 import org.jetbrains.jet.lang.resolve.name.FqName; 043 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 044 import org.jetbrains.jet.utils.KotlinVfsUtil; 045 import org.jetbrains.jet.utils.UtilsPackage; 046 047 import java.io.File; 048 import java.net.MalformedURLException; 049 import java.net.URL; 050 import java.util.*; 051 052 public class LightClassUtil { 053 private static final Logger LOG = Logger.getInstance(LightClassUtil.class); 054 055 public static final File BUILT_INS_SRC_DIR = new File("core/builtins/native", KotlinBuiltIns.BUILT_INS_PACKAGE_NAME_STRING); 056 057 /** 058 * Checks whether the given file is loaded from the location where Kotlin's built-in classes are defined. 059 * As of today, this is core/builtins/native/kotlin directory and files such as Any.kt, Nothing.kt etc. 060 * 061 * Used to skip JetLightClass creation for built-ins, because built-in classes have no Java counterparts 062 */ 063 public static boolean belongsToKotlinBuiltIns(@NotNull JetFile file) { 064 VirtualFile virtualFile = file.getVirtualFile(); 065 if (virtualFile != null) { 066 VirtualFile parent = virtualFile.getParent(); 067 if (parent != null) { 068 try { 069 String jetVfsPathUrl = KotlinVfsUtil.convertFromUrl(getBuiltInsDirUrl()); 070 String fileDirVfsUrl = parent.getUrl(); 071 if (jetVfsPathUrl.equals(fileDirVfsUrl)) { 072 return true; 073 } 074 } 075 catch (MalformedURLException e) { 076 LOG.error(e); 077 } 078 } 079 } 080 081 // We deliberately return false on error: who knows what weird URLs we might come across out there 082 // it would be a pity if no light classes would be created in such cases 083 return false; 084 } 085 086 @NotNull 087 public static URL getBuiltInsDirUrl() { 088 String builtInFilePath = "/" + KotlinBuiltIns.BUILT_INS_PACKAGE_NAME_STRING + "/Library.kt"; 089 090 URL url = KotlinBuiltIns.class.getResource(builtInFilePath); 091 092 if (url == null) { 093 if (ApplicationManager.getApplication().isUnitTestMode()) { 094 // HACK: Temp code. Get built-in files from the sources when running from test. 095 try { 096 return new URL(StandardFileSystems.FILE_PROTOCOL, "", 097 FileUtil.toSystemIndependentName(BUILT_INS_SRC_DIR.getAbsolutePath())); 098 } 099 catch (MalformedURLException e) { 100 throw UtilsPackage.rethrow(e); 101 } 102 } 103 104 throw new IllegalStateException("Built-ins file wasn't found at url: " + builtInFilePath); 105 } 106 107 try { 108 return new URL(url.getProtocol(), url.getHost(), PathUtil.getParentPath(url.getFile())); 109 } 110 catch (MalformedURLException e) { 111 throw new AssertionError(e); 112 } 113 } 114 115 @Nullable 116 /*package*/ static PsiClass findClass(@NotNull FqName fqn, @NotNull StubElement<?> stub) { 117 if (stub instanceof PsiClassStub && Comparing.equal(fqn.asString(), ((PsiClassStub) stub).getQualifiedName())) { 118 return (PsiClass) stub.getPsi(); 119 } 120 121 if (stub instanceof PsiClassStub || stub instanceof PsiFileStub) { 122 for (StubElement child : stub.getChildrenStubs()) { 123 PsiClass answer = findClass(fqn, child); 124 if (answer != null) return answer; 125 } 126 } 127 128 return null; 129 } 130 131 @Nullable 132 public static PsiClass getPsiClass(@Nullable JetClassOrObject classOrObject) { 133 if (classOrObject == null) return null; 134 return LightClassGenerationSupport.getInstance(classOrObject.getProject()).getPsiClass(classOrObject); 135 } 136 137 @Nullable 138 public static PsiMethod getLightClassAccessorMethod(@NotNull JetPropertyAccessor accessor) { 139 return getPsiMethodWrapper(accessor); 140 } 141 142 @NotNull 143 public static PropertyAccessorsPsiMethods getLightClassPropertyMethods(@NotNull JetProperty property) { 144 JetPropertyAccessor getter = property.getGetter(); 145 JetPropertyAccessor setter = property.getSetter(); 146 147 PsiMethod getterWrapper = getter != null ? getLightClassAccessorMethod(getter) : null; 148 PsiMethod setterWrapper = setter != null ? getLightClassAccessorMethod(setter) : null; 149 150 return extractPropertyAccessors(property, getterWrapper, setterWrapper); 151 } 152 153 @NotNull 154 public static PropertyAccessorsPsiMethods getLightClassPropertyMethods(@NotNull JetParameter parameter) { 155 return extractPropertyAccessors(parameter, null, null); 156 } 157 158 @Nullable 159 public static PsiMethod getLightClassMethod(@NotNull JetNamedFunction function) { 160 return getPsiMethodWrapper(function); 161 } 162 163 @Nullable 164 private static PsiMethod getPsiMethodWrapper(@NotNull JetDeclaration declaration) { 165 List<PsiMethod> wrappers = getPsiMethodWrappers(declaration, false); 166 return !wrappers.isEmpty() ? wrappers.get(0) : null; 167 } 168 169 @NotNull 170 private static List<PsiMethod> getPsiMethodWrappers(@NotNull JetDeclaration declaration, boolean collectAll) { 171 PsiClass psiClass = getWrappingClass(declaration); 172 if (psiClass == null) { 173 return Collections.emptyList(); 174 } 175 176 List<PsiMethod> methods = new SmartList<PsiMethod>(); 177 for (PsiMethod method : psiClass.getMethods()) { 178 try { 179 if (method instanceof KotlinLightMethod && ((KotlinLightMethod) method).getOrigin() == declaration) { 180 methods.add(method); 181 if (!collectAll) { 182 return methods; 183 } 184 } 185 } 186 catch (ProcessCanceledException e) { 187 throw e; 188 } 189 catch (Throwable e) { 190 throw new IllegalStateException( 191 "Error while wrapping declaration " + declaration.getName() + 192 "Context\n:" + 193 String.format("=== In file ===\n" + 194 "%s\n" + 195 "=== On element ===\n" + 196 "%s\n" + 197 "=== WrappedElement ===\n" + 198 "%s\n", 199 declaration.getContainingFile().getText(), 200 declaration.getText(), 201 method.toString()), 202 e 203 ); 204 } 205 } 206 207 return methods; 208 } 209 210 @Nullable 211 private static PsiClass getWrappingClass(@NotNull JetDeclaration declaration) { 212 if (declaration instanceof JetParameter) { 213 JetClass constructorClass = JetPsiUtil.getClassIfParameterIsProperty((JetParameter) declaration); 214 if (constructorClass != null) { 215 return getPsiClass(constructorClass); 216 } 217 } 218 219 if (declaration instanceof JetPropertyAccessor) { 220 PsiElement propertyParent = declaration.getParent(); 221 assert propertyParent instanceof JetProperty : "JetProperty is expected to be parent of accessor"; 222 223 declaration = (JetProperty) propertyParent; 224 } 225 226 //noinspection unchecked 227 if (PsiTreeUtil.getParentOfType(declaration, JetFunction.class, JetProperty.class) != null) { 228 // Can't get wrappers for internal declarations. Their classes are not generated during calcStub 229 // with ClassBuilderMode.LIGHT_CLASSES mode, and this produces "Class not found exception" in getDelegate() 230 return null; 231 } 232 233 PsiElement parent = declaration.getParent(); 234 235 if (parent instanceof JetFile) { 236 // top-level declaration 237 FqName fqName = getPackageClassNameForFile((JetFile) parent); 238 if (fqName != null) { 239 Project project = declaration.getProject(); 240 241 return JavaElementFinder.getInstance(project).findClass(fqName.asString(), GlobalSearchScope.allScope(project)); 242 } 243 } 244 else if (parent instanceof JetClassBody) { 245 assert parent.getParent() instanceof JetClassOrObject; 246 return getPsiClass((JetClassOrObject) parent.getParent()); 247 } 248 249 return null; 250 } 251 252 @Nullable 253 private static FqName getPackageClassNameForFile(@NotNull JetFile jetFile) { 254 String packageName = jetFile.getPackageName(); 255 return packageName == null ? null : PackageClassUtils.getPackageClassFqName(new FqName(packageName)); 256 } 257 258 @NotNull 259 private static PropertyAccessorsPsiMethods extractPropertyAccessors( 260 @NotNull JetDeclaration jetDeclaration, 261 @Nullable PsiMethod specialGetter, @Nullable PsiMethod specialSetter 262 ) { 263 PsiMethod getterWrapper = specialGetter; 264 PsiMethod setterWrapper = specialSetter; 265 266 if (getterWrapper == null || setterWrapper == null) { 267 // If some getter or setter isn't found yet try to get it from wrappers for general declaration 268 269 List<PsiMethod> wrappers = getPsiMethodWrappers(jetDeclaration, true); 270 assert wrappers.size() <= 2 : "Maximum two wrappers are expected to be generated for declaration: " + jetDeclaration.getText(); 271 272 for (PsiMethod wrapper : wrappers) { 273 if (wrapper.getName().startsWith(JvmAbi.SETTER_PREFIX)) { 274 assert setterWrapper == null : String.format( 275 "Setter accessor isn't expected to be reassigned (old: %s, new: %s)", setterWrapper, wrapper); 276 277 setterWrapper = wrapper; 278 } 279 else { 280 assert getterWrapper == null : String.format( 281 "Getter accessor isn't expected to be reassigned (old: %s, new: %s)", getterWrapper, wrapper); 282 283 getterWrapper = wrapper; 284 } 285 } 286 } 287 288 return new PropertyAccessorsPsiMethods(getterWrapper, setterWrapper); 289 } 290 291 @NotNull 292 public static PsiTypeParameterList buildLightTypeParameterList( 293 PsiTypeParameterListOwner owner, 294 JetDeclaration declaration) { 295 LightTypeParameterListBuilder builder = new LightTypeParameterListBuilder(owner.getManager(), owner.getLanguage()); 296 if (declaration instanceof JetTypeParameterListOwner) { 297 JetTypeParameterListOwner typeParameterListOwner = (JetTypeParameterListOwner) declaration; 298 List<JetTypeParameter> parameters = typeParameterListOwner.getTypeParameters(); 299 for (int i = 0; i < parameters.size(); i++) { 300 JetTypeParameter jetTypeParameter = parameters.get(i); 301 String name = jetTypeParameter.getName(); 302 String safeName = name == null ? "__no_name__" : name; 303 builder.addParameter(new KotlinLightTypeParameter(owner, i, safeName)); 304 } 305 } 306 return builder; 307 } 308 309 public static class PropertyAccessorsPsiMethods implements Iterable<PsiMethod> { 310 private final PsiMethod getter; 311 private final PsiMethod setter; 312 private final Collection<PsiMethod> accessors = new ArrayList<PsiMethod>(2); 313 314 PropertyAccessorsPsiMethods(@Nullable PsiMethod getter, @Nullable PsiMethod setter) { 315 this.getter = getter; 316 if (getter != null) { 317 accessors.add(getter); 318 } 319 320 this.setter = setter; 321 if (setter != null) { 322 accessors.add(setter); 323 } 324 } 325 326 @Nullable 327 public PsiMethod getGetter() { 328 return getter; 329 } 330 331 @Nullable 332 public PsiMethod getSetter() { 333 return setter; 334 } 335 336 @NotNull 337 @Override 338 public Iterator<PsiMethod> iterator() { 339 return accessors.iterator(); 340 } 341 } 342 343 private LightClassUtil() { 344 } 345 }