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