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