org.jetbrains.kotlin.js.patterns.PatternBuilder Maven / Gradle / Ivy
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.patterns;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.idea.KotlinLanguage;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.OverrideResolver;
import java.util.Arrays;
import java.util.List;
public final class PatternBuilder {
@NotNull
private static final NamePredicate KOTLIN_NAME_PREDICATE = new NamePredicate("kotlin");
@NotNull
private static final Name KOTLIN_NAME = Name.identifier(KotlinLanguage.NAME.toLowerCase());
private PatternBuilder() {
}
@NotNull
public static DescriptorPredicate pattern(@NotNull NamePredicate checker, @NotNull String stringWithPattern) {
List checkers = Lists.newArrayList(checker);
checkers.addAll(parseFqNamesFromString(stringWithPattern));
return pattern(checkers, parseArgumentsFromString(stringWithPattern));
}
@NotNull
public static DescriptorPredicate pattern(@NotNull String stringWithPattern, @NotNull NamePredicate checker) {
List checkers = Lists.newArrayList(parseFqNamesFromString(stringWithPattern));
checkers.add(checker);
return pattern(checkers);
}
@NotNull
public static DescriptorPredicate pattern(@NotNull String stringWithPattern) {
return pattern(parseFqNamesFromString(stringWithPattern), parseArgumentsFromString(stringWithPattern));
}
@NotNull
private static List parseFqNamesFromString(@NotNull String stringWithPattern) {
stringWithPattern = getNamePatternFromString(stringWithPattern);
String[] subPatterns = stringWithPattern.split("\\.");
List checkers = Lists.newArrayList();
for (String subPattern : subPatterns) {
String[] validNames = subPattern.split("\\|");
checkers.add(new NamePredicate(validNames));
}
return checkers;
}
@Nullable
private static List parseArgumentsFromString(@NotNull String stringWithPattern) {
stringWithPattern = getArgumentsPatternFromString(stringWithPattern);
if (stringWithPattern == null) return null;
List checkers = Lists.newArrayList();
if (stringWithPattern.isEmpty()) {
return checkers;
}
String[] subPatterns = stringWithPattern.split("\\,");
for (String subPattern : subPatterns) {
String[] validNames = subPattern.split("\\|");
checkers.add(new NamePredicate(validNames));
}
return checkers;
}
@NotNull
private static String getNamePatternFromString(@NotNull String stringWithPattern) {
int left = stringWithPattern.indexOf("(");
if (left < 0) {
return stringWithPattern;
}
else {
return stringWithPattern.substring(0, left);
}
}
@Nullable
private static String getArgumentsPatternFromString(@NotNull String stringWithPattern) {
int left = stringWithPattern.indexOf("(");
if (left < 0) {
return null;
}
else {
int right = stringWithPattern.indexOf(")");
assert right == stringWithPattern.length() - 1 : "expected ')' at the end: " + stringWithPattern;
return stringWithPattern.substring(left + 1, right);
}
}
@NotNull
private static DescriptorPredicate pattern(@NotNull List checkers) {
return pattern(checkers, null);
}
@NotNull
private static DescriptorPredicate pattern(@NotNull List checkers, @Nullable List arguments) {
assert !checkers.isEmpty();
final List checkersWithPrefixChecker = Lists.newArrayList();
if (!checkers.get(0).apply(KOTLIN_NAME)) {
checkersWithPrefixChecker.add(KOTLIN_NAME_PREDICATE);
}
checkersWithPrefixChecker.addAll(checkers);
assert checkersWithPrefixChecker.size() > 1;
final List argumentCheckers = arguments != null ? Lists.newArrayList(arguments) : null;
return new DescriptorPredicate() {
@Override
public boolean apply(@Nullable FunctionDescriptor descriptor) {
assert descriptor != null : "argument for DescriptorPredicate.apply should not be null, checkers=" + checkersWithPrefixChecker;
//TODO: no need to wrap if we check beforehand
try {
return doApply(descriptor);
}
catch (IllegalArgumentException e) {
return false;
}
}
private boolean doApply(@NotNull FunctionDescriptor descriptor) {
List nameParts = DescriptorUtils.getFqName(descriptor).pathSegments();
if (nameParts.size() != checkersWithPrefixChecker.size()) return false;
return allNamePartsValid(nameParts) && checkAllArgumentsValidIfNeeded(descriptor);
}
private boolean checkAllArgumentsValidIfNeeded(@NotNull FunctionDescriptor descriptor) {
if (argumentCheckers != null) {
List valueParameterDescriptors = descriptor.getValueParameters();
if (valueParameterDescriptors.size() != argumentCheckers.size()) {
return false;
}
for (int i = 0; i < valueParameterDescriptors.size(); i++) {
ValueParameterDescriptor valueParameterDescriptor = valueParameterDescriptors.get(i);
Name name = DescriptorUtilsKt.getNameIfStandardType(valueParameterDescriptor.getType());
NamePredicate namePredicate = argumentCheckers.get(i);
if (!namePredicate.apply(name)) return false;
}
}
return true;
}
private boolean allNamePartsValid(@NotNull List nameParts) {
for (int i = 0; i < nameParts.size(); ++i) {
Name namePart = nameParts.get(i);
NamePredicate correspondingPredicate = checkersWithPrefixChecker.get(i);
if (!correspondingPredicate.apply(namePart)) {
return false;
}
}
return true;
}
};
}
@NotNull
public static DescriptorPredicate pattern(@NotNull NamePredicate... checkers) {
return pattern(Arrays.asList(checkers));
}
@NotNull
public static DescriptorPredicateImpl pattern(@NotNull String... names) {
return new DescriptorPredicateImpl(names);
}
public static class DescriptorPredicateImpl implements DescriptorPredicate {
private final String[] names;
private String receiverFqName;
private boolean checkOverridden;
public DescriptorPredicateImpl(String... names) {
this.names = names;
}
public DescriptorPredicateImpl isExtensionOf(String receiverFqName) {
this.receiverFqName = receiverFqName;
return this;
}
public DescriptorPredicateImpl checkOverridden() {
this.checkOverridden = true;
return this;
}
private boolean matches(@NotNull CallableDescriptor callable) {
DeclarationDescriptor descriptor = callable;
int nameIndex = names.length - 1;
while (true) {
if (nameIndex == -1) {
return false;
}
if (!descriptor.getName().asString().equals(names[nameIndex])) {
return false;
}
nameIndex--;
descriptor = descriptor.getContainingDeclaration();
if (descriptor instanceof PackageFragmentDescriptor) {
return nameIndex == 0 && names[0].equals(((PackageFragmentDescriptor) descriptor).getFqName().asString());
}
}
}
@Override
public boolean apply(@Nullable FunctionDescriptor functionDescriptor) {
assert functionDescriptor != null :
"argument for DescriptorPredicate.apply should not be null, receiverFqName=" + receiverFqName + " names=" + Arrays.asList(names);
ReceiverParameterDescriptor actualReceiver = functionDescriptor.getExtensionReceiverParameter();
if (actualReceiver != null) {
if (receiverFqName == null) return false;
String actualReceiverFqName = DescriptorUtilsKt.getJetTypeFqName(actualReceiver.getType(), false);
if (!actualReceiverFqName.equals(receiverFqName)) return false;
}
if (!(functionDescriptor.getContainingDeclaration() instanceof ClassDescriptor)) {
return matches(functionDescriptor);
}
for (CallableMemberDescriptor real : OverrideResolver.getOverriddenDeclarations(functionDescriptor)) {
if (matches(real)) {
return true;
}
}
if (checkOverridden) {
for (CallableDescriptor overridden : DescriptorUtils.getAllOverriddenDescriptors(functionDescriptor)) {
if (matches(overridden)) {
return true;
}
}
}
return false;
}
}
}