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 }