| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.jetbrains.python.psi.impl; |
| |
| import com.intellij.psi.PsiFile; |
| import com.intellij.util.ArrayUtil; |
| import com.jetbrains.python.PyNames; |
| import com.jetbrains.python.psi.*; |
| import com.jetbrains.python.psi.resolve.PyResolveUtil; |
| import com.jetbrains.python.psi.resolve.ResolveProcessor; |
| import com.jetbrains.python.toolbox.Maybe; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * Something that describes a property, with all related accessors. |
| * <br/> |
| * User: dcheryasov |
| * Date: Jun 3, 2010 2:07:48 PM |
| */ |
| public abstract class PropertyBunch<MType> { |
| |
| protected Maybe<MType> myGetter; |
| protected Maybe<MType> mySetter; |
| protected Maybe<MType> myDeleter; |
| protected String myDoc; |
| protected PyTargetExpression mySite; |
| |
| @NotNull |
| public Maybe<MType> getGetter() { |
| return myGetter; |
| } |
| |
| @NotNull |
| public Maybe<MType> getSetter() { |
| return mySetter; |
| } |
| |
| @NotNull |
| public Maybe<MType> getDeleter() { |
| return myDeleter; |
| } |
| |
| @Nullable |
| public String getDoc() { |
| return myDoc; |
| } |
| |
| |
| /** |
| * @param ref a reference as an argument in property() call |
| * @return value we want to store (resolved callable, name, etc) |
| */ |
| @NotNull |
| protected abstract Maybe<MType> translate(@Nullable PyExpression ref); |
| |
| @Nullable |
| public static PyCallExpression findPropertyCallSite(@Nullable PyExpression source) { |
| if (source instanceof PyCallExpression) { |
| final PyCallExpression call = (PyCallExpression)source; |
| PyExpression callee = call.getCallee(); |
| if (callee instanceof PyReferenceExpression) { |
| PyReferenceExpression ref = (PyReferenceExpression)callee; |
| if (ref.isQualified()) return null; |
| if (PyNames.PROPERTY.equals(callee.getName())) { |
| PsiFile file = source.getContainingFile(); |
| if (isBuiltinFile(file) || !resolvesLocally(ref)) { |
| // we assume that a non-local name 'property' is a built-in name. |
| // ref.resolve() is not used because we run in stub building phase where resolve() is frowned upon. |
| // NOTE: this logic fails if (quite unusually) name 'property' is directly imported from builtins. |
| return call; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| private static boolean isBuiltinFile(PsiFile file) { |
| final String name = file.getName(); |
| return PyBuiltinCache.BUILTIN_FILE.equals(name) || PyBuiltinCache.BUILTIN_FILE_3K.equals(name); |
| } |
| |
| /** |
| * Resolve in containing file only. |
| * @param ref what to resolve |
| * @return true iff ref obviously resolves to a local name (maybe partially, e.g. via import). |
| */ |
| protected static boolean resolvesLocally(@NotNull PyReferenceExpression ref) { |
| final String name = ref.getName(); |
| if (name != null) { |
| final ResolveProcessor processor = new ResolveProcessor(name); |
| processor.setLocalResolve(); |
| PyResolveUtil.scopeCrawlUp(processor, ref, name, null); |
| return processor.getResult() != null; |
| } |
| return false; |
| } |
| |
| /** |
| * Tries to form a bunch from data available at a possible property() call site. |
| * @param source should be a PyCallExpression (if not, null is immediately returned). |
| * @param target what to fill with data (return type contravariance prevents us from creating it inside). |
| * @return true if target was successfully filled. |
| */ |
| protected static <MType> boolean fillFromCall(PyExpression source, PropertyBunch<MType> target) { |
| PyCallExpression call = findPropertyCallSite(source); |
| if (call != null) { |
| PyArgumentList arglist = call.getArgumentList(); |
| if (arglist != null) { |
| PyExpression[] accessors = new PyExpression[3]; |
| String doc = null; |
| int position = 0; |
| String[] keywords = new String[] { "fget", "fset", "fdel", "doc" }; |
| for (PyExpression arg: arglist.getArguments()) { |
| int index = -1; |
| if (arg instanceof PyKeywordArgument) { |
| String keyword = ((PyKeywordArgument)arg).getKeyword(); |
| index = ArrayUtil.indexOf(keywords, keyword); |
| if (index < 0) { |
| continue; |
| } |
| position = -1; |
| } |
| else if (position >= 0) { |
| index = position; |
| position++; |
| } |
| |
| if (index >= 0) { |
| arg = PyUtil.peelArgument(arg); |
| if (index < 3) { |
| accessors [index] = arg; |
| } |
| else if (index == 3 && arg instanceof PyStringLiteralExpression) { |
| doc = ((PyStringLiteralExpression)arg).getStringValue(); |
| } |
| } |
| } |
| target.myGetter = target.translate(accessors[0]); |
| target.mySetter = target.translate(accessors[1]); |
| target.myDeleter = target.translate(accessors[2]); |
| target.myDoc = doc; |
| return true; |
| } |
| } |
| return false; |
| } |
| } |