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 }