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.asString());
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 + "/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 = PackageClassUtils.getPackageClassFqName(((JetFile) parent).getPackageFqName());
238 Project project = declaration.getProject();
239 return JavaElementFinder.getInstance(project).findClass(fqName.asString(), GlobalSearchScope.allScope(project));
240 }
241 else if (parent instanceof JetClassBody) {
242 assert parent.getParent() instanceof JetClassOrObject;
243 return getPsiClass((JetClassOrObject) parent.getParent());
244 }
245
246 return null;
247 }
248
249 @NotNull
250 private static PropertyAccessorsPsiMethods extractPropertyAccessors(
251 @NotNull JetDeclaration jetDeclaration,
252 @Nullable PsiMethod specialGetter, @Nullable PsiMethod specialSetter
253 ) {
254 PsiMethod getterWrapper = specialGetter;
255 PsiMethod setterWrapper = specialSetter;
256
257 if (getterWrapper == null || setterWrapper == null) {
258 // If some getter or setter isn't found yet try to get it from wrappers for general declaration
259
260 List<PsiMethod> wrappers = getPsiMethodWrappers(jetDeclaration, true);
261 assert wrappers.size() <= 2 : "Maximum two wrappers are expected to be generated for declaration: " + jetDeclaration.getText();
262
263 for (PsiMethod wrapper : wrappers) {
264 if (wrapper.getName().startsWith(JvmAbi.SETTER_PREFIX)) {
265 assert setterWrapper == null : String.format(
266 "Setter accessor isn't expected to be reassigned (old: %s, new: %s)", setterWrapper, wrapper);
267
268 setterWrapper = wrapper;
269 }
270 else {
271 assert getterWrapper == null : String.format(
272 "Getter accessor isn't expected to be reassigned (old: %s, new: %s)", getterWrapper, wrapper);
273
274 getterWrapper = wrapper;
275 }
276 }
277 }
278
279 return new PropertyAccessorsPsiMethods(getterWrapper, setterWrapper);
280 }
281
282 @NotNull
283 public static PsiTypeParameterList buildLightTypeParameterList(
284 PsiTypeParameterListOwner owner,
285 JetDeclaration declaration) {
286 LightTypeParameterListBuilder builder = new LightTypeParameterListBuilder(owner.getManager(), owner.getLanguage());
287 if (declaration instanceof JetTypeParameterListOwner) {
288 JetTypeParameterListOwner typeParameterListOwner = (JetTypeParameterListOwner) declaration;
289 List<JetTypeParameter> parameters = typeParameterListOwner.getTypeParameters();
290 for (int i = 0; i < parameters.size(); i++) {
291 JetTypeParameter jetTypeParameter = parameters.get(i);
292 String name = jetTypeParameter.getName();
293 String safeName = name == null ? "__no_name__" : name;
294 builder.addParameter(new KotlinLightTypeParameter(owner, i, safeName));
295 }
296 }
297 return builder;
298 }
299
300 public static class PropertyAccessorsPsiMethods implements Iterable<PsiMethod> {
301 private final PsiMethod getter;
302 private final PsiMethod setter;
303 private final Collection<PsiMethod> accessors = new ArrayList<PsiMethod>(2);
304
305 PropertyAccessorsPsiMethods(@Nullable PsiMethod getter, @Nullable PsiMethod setter) {
306 this.getter = getter;
307 if (getter != null) {
308 accessors.add(getter);
309 }
310
311 this.setter = setter;
312 if (setter != null) {
313 accessors.add(setter);
314 }
315 }
316
317 @Nullable
318 public PsiMethod getGetter() {
319 return getter;
320 }
321
322 @Nullable
323 public PsiMethod getSetter() {
324 return setter;
325 }
326
327 @NotNull
328 @Override
329 public Iterator<PsiMethod> iterator() {
330 return accessors.iterator();
331 }
332 }
333
334 private LightClassUtil() {
335 }
336 }