001 /*
002 * Copyright 2010-2015 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.kotlin.js.patterns;
018
019 import com.google.common.collect.Lists;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.descriptors.*;
023 import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt;
024 import org.jetbrains.kotlin.name.Name;
025 import org.jetbrains.kotlin.idea.KotlinLanguage;
026 import org.jetbrains.kotlin.resolve.DescriptorUtils;
027 import org.jetbrains.kotlin.resolve.OverrideResolver;
028
029 import java.util.Arrays;
030 import java.util.List;
031
032 public final class PatternBuilder {
033
034 @NotNull
035 private static final NamePredicate KOTLIN_NAME_PREDICATE = new NamePredicate("kotlin");
036
037 @NotNull
038 private static final Name KOTLIN_NAME = Name.identifier(KotlinLanguage.NAME.toLowerCase());
039
040 private PatternBuilder() {
041 }
042
043 @NotNull
044 public static DescriptorPredicate pattern(@NotNull NamePredicate checker, @NotNull String stringWithPattern) {
045 List<NamePredicate> checkers = Lists.newArrayList(checker);
046 checkers.addAll(parseFqNamesFromString(stringWithPattern));
047 return pattern(checkers, parseArgumentsFromString(stringWithPattern));
048 }
049
050 @NotNull
051 public static DescriptorPredicate pattern(@NotNull String stringWithPattern, @NotNull NamePredicate checker) {
052 List<NamePredicate> checkers = Lists.newArrayList(parseFqNamesFromString(stringWithPattern));
053 checkers.add(checker);
054 return pattern(checkers);
055 }
056
057 @NotNull
058 public static DescriptorPredicate pattern(@NotNull String stringWithPattern) {
059 return pattern(parseFqNamesFromString(stringWithPattern), parseArgumentsFromString(stringWithPattern));
060 }
061
062 @NotNull
063 private static List<NamePredicate> parseFqNamesFromString(@NotNull String stringWithPattern) {
064 stringWithPattern = getNamePatternFromString(stringWithPattern);
065 String[] subPatterns = stringWithPattern.split("\\.");
066 List<NamePredicate> checkers = Lists.newArrayList();
067 for (String subPattern : subPatterns) {
068 String[] validNames = subPattern.split("\\|");
069 checkers.add(new NamePredicate(validNames));
070 }
071 return checkers;
072 }
073
074 @Nullable
075 private static List<NamePredicate> parseArgumentsFromString(@NotNull String stringWithPattern) {
076 stringWithPattern = getArgumentsPatternFromString(stringWithPattern);
077 if (stringWithPattern == null) return null;
078
079 List<NamePredicate> checkers = Lists.newArrayList();
080 if (stringWithPattern.isEmpty()) {
081 return checkers;
082 }
083
084 String[] subPatterns = stringWithPattern.split("\\,");
085 for (String subPattern : subPatterns) {
086 String[] validNames = subPattern.split("\\|");
087 checkers.add(new NamePredicate(validNames));
088 }
089 return checkers;
090 }
091
092 @NotNull
093 private static String getNamePatternFromString(@NotNull String stringWithPattern) {
094 int left = stringWithPattern.indexOf("(");
095 if (left < 0) {
096 return stringWithPattern;
097 }
098 else {
099 return stringWithPattern.substring(0, left);
100 }
101 }
102
103 @Nullable
104 private static String getArgumentsPatternFromString(@NotNull String stringWithPattern) {
105 int left = stringWithPattern.indexOf("(");
106 if (left < 0) {
107 return null;
108 }
109 else {
110 int right = stringWithPattern.indexOf(")");
111 assert right == stringWithPattern.length() - 1 : "expected ')' at the end: " + stringWithPattern;
112 return stringWithPattern.substring(left + 1, right);
113 }
114 }
115
116 @NotNull
117 private static DescriptorPredicate pattern(@NotNull List<NamePredicate> checkers) {
118 return pattern(checkers, null);
119 }
120
121 @NotNull
122 private static DescriptorPredicate pattern(@NotNull List<NamePredicate> checkers, @Nullable List<NamePredicate> arguments) {
123 assert !checkers.isEmpty();
124 final List<NamePredicate> checkersWithPrefixChecker = Lists.newArrayList();
125 if (!checkers.get(0).apply(KOTLIN_NAME)) {
126 checkersWithPrefixChecker.add(KOTLIN_NAME_PREDICATE);
127 }
128
129 checkersWithPrefixChecker.addAll(checkers);
130
131 assert checkersWithPrefixChecker.size() > 1;
132
133 final List<NamePredicate> argumentCheckers = arguments != null ? Lists.newArrayList(arguments) : null;
134
135 return new DescriptorPredicate() {
136 @Override
137 public boolean apply(@Nullable FunctionDescriptor descriptor) {
138 assert descriptor != null : "argument for DescriptorPredicate.apply should not be null, checkers=" + checkersWithPrefixChecker;
139 //TODO: no need to wrap if we check beforehand
140 try {
141 return doApply(descriptor);
142 }
143 catch (IllegalArgumentException e) {
144 return false;
145 }
146 }
147
148 private boolean doApply(@NotNull FunctionDescriptor descriptor) {
149 List<Name> nameParts = DescriptorUtils.getFqName(descriptor).pathSegments();
150 if (nameParts.size() != checkersWithPrefixChecker.size()) return false;
151
152 return allNamePartsValid(nameParts) && checkAllArgumentsValidIfNeeded(descriptor);
153 }
154
155 private boolean checkAllArgumentsValidIfNeeded(@NotNull FunctionDescriptor descriptor) {
156 if (argumentCheckers != null) {
157 List<ValueParameterDescriptor> valueParameterDescriptors = descriptor.getValueParameters();
158 if (valueParameterDescriptors.size() != argumentCheckers.size()) {
159 return false;
160 }
161 for (int i = 0; i < valueParameterDescriptors.size(); i++) {
162 ValueParameterDescriptor valueParameterDescriptor = valueParameterDescriptors.get(i);
163 Name name = DescriptorUtilsKt.getNameIfStandardType(valueParameterDescriptor.getType());
164 NamePredicate namePredicate = argumentCheckers.get(i);
165 if (!namePredicate.apply(name)) return false;
166 }
167 }
168 return true;
169 }
170
171 private boolean allNamePartsValid(@NotNull List<Name> nameParts) {
172 for (int i = 0; i < nameParts.size(); ++i) {
173 Name namePart = nameParts.get(i);
174 NamePredicate correspondingPredicate = checkersWithPrefixChecker.get(i);
175 if (!correspondingPredicate.apply(namePart)) {
176 return false;
177 }
178 }
179 return true;
180 }
181 };
182 }
183
184 @NotNull
185 public static DescriptorPredicate pattern(@NotNull NamePredicate... checkers) {
186 return pattern(Arrays.asList(checkers));
187 }
188
189 @NotNull
190 public static DescriptorPredicateImpl pattern(@NotNull String... names) {
191 return new DescriptorPredicateImpl(names);
192 }
193
194 public static class DescriptorPredicateImpl implements DescriptorPredicate {
195 private final String[] names;
196
197 private String receiverFqName;
198
199 private boolean checkOverridden;
200
201 public DescriptorPredicateImpl(String... names) {
202 this.names = names;
203 }
204
205 public DescriptorPredicateImpl isExtensionOf(String receiverFqName) {
206 this.receiverFqName = receiverFqName;
207 return this;
208 }
209
210 public DescriptorPredicateImpl checkOverridden() {
211 this.checkOverridden = true;
212 return this;
213 }
214
215 private boolean matches(@NotNull CallableDescriptor callable) {
216 DeclarationDescriptor descriptor = callable;
217 int nameIndex = names.length - 1;
218 while (true) {
219 if (nameIndex == -1) {
220 return false;
221 }
222
223 if (!descriptor.getName().asString().equals(names[nameIndex])) {
224 return false;
225 }
226
227 nameIndex--;
228 descriptor = descriptor.getContainingDeclaration();
229 if (descriptor instanceof PackageFragmentDescriptor) {
230 return nameIndex == 0 && names[0].equals(((PackageFragmentDescriptor) descriptor).getFqName().asString());
231 }
232 }
233 }
234
235 @Override
236 public boolean apply(@Nullable FunctionDescriptor functionDescriptor) {
237 assert functionDescriptor != null :
238 "argument for DescriptorPredicate.apply should not be null, receiverFqName=" + receiverFqName + " names=" + Arrays.asList(names);
239 ReceiverParameterDescriptor actualReceiver = functionDescriptor.getExtensionReceiverParameter();
240 if (actualReceiver != null) {
241 if (receiverFqName == null) return false;
242
243 String actualReceiverFqName = DescriptorUtilsKt.getJetTypeFqName(actualReceiver.getType(), false);
244
245 if (!actualReceiverFqName.equals(receiverFqName)) return false;
246 }
247
248 if (!(functionDescriptor.getContainingDeclaration() instanceof ClassDescriptor)) {
249 return matches(functionDescriptor);
250 }
251
252 for (CallableMemberDescriptor real : OverrideResolver.getOverriddenDeclarations(functionDescriptor)) {
253 if (matches(real)) {
254 return true;
255 }
256 }
257
258 if (checkOverridden) {
259 for (CallableDescriptor overridden : DescriptorUtils.getAllOverriddenDescriptors(functionDescriptor)) {
260 if (matches(overridden)) {
261 return true;
262 }
263 }
264 }
265
266 return false;
267 }
268 }
269 }