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