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.jet.lang.descriptors.*;
022 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
023 import org.jetbrains.jet.lang.resolve.OverrideResolver;
024 import org.jetbrains.jet.lang.resolve.name.Name;
025 import org.jetbrains.k2js.translate.context.Namer;
026
027 import java.util.Arrays;
028 import java.util.List;
029
030 public final class PatternBuilder {
031
032 @NotNull
033 private static final NamePredicate KOTLIN_NAME_PREDICATE = new NamePredicate("kotlin");
034
035 @NotNull
036 private static final Name KOTLIN_NAME = Name.identifier(Namer.KOTLIN_LOWER_NAME);
037
038 private PatternBuilder() {
039 }
040
041 @NotNull
042 public static DescriptorPredicate pattern(@NotNull NamePredicate checker, @NotNull String stringWithPattern) {
043 List<NamePredicate> checkers = Lists.newArrayList(checker);
044 checkers.addAll(parseStringAsCheckerList(stringWithPattern));
045 return pattern(checkers);
046 }
047
048 @NotNull
049 public static DescriptorPredicate pattern(@NotNull String stringWithPattern, @NotNull NamePredicate checker) {
050 List<NamePredicate> checkers = Lists.newArrayList(parseStringAsCheckerList(stringWithPattern));
051 checkers.add(checker);
052 return pattern(checkers);
053 }
054
055 @NotNull
056 public static DescriptorPredicate pattern(@NotNull String string) {
057 List<NamePredicate> checkers = parseStringAsCheckerList(string);
058 return pattern(checkers);
059 }
060
061 @NotNull
062 private static List<NamePredicate> parseStringAsCheckerList(@NotNull String stringWithPattern) {
063 String[] subPatterns = stringWithPattern.split("\\.");
064 List<NamePredicate> checkers = Lists.newArrayList();
065 for (String subPattern : subPatterns) {
066 String[] validNames = subPattern.split("\\|");
067 checkers.add(new NamePredicate(validNames));
068 }
069 return checkers;
070 }
071
072 @NotNull
073 private static DescriptorPredicate pattern(@NotNull List<NamePredicate> checkers) {
074 assert !checkers.isEmpty();
075 final List<NamePredicate> checkersWithPrefixChecker = Lists.newArrayList();
076 if (!checkers.get(0).apply(KOTLIN_NAME)) {
077 checkersWithPrefixChecker.add(KOTLIN_NAME_PREDICATE);
078 }
079
080 checkersWithPrefixChecker.addAll(checkers);
081
082 assert checkersWithPrefixChecker.size() > 1;
083
084 return new DescriptorPredicate() {
085 @Override
086 public boolean apply(@NotNull FunctionDescriptor descriptor) {
087 //TODO: no need to wrap if we check beforehand
088 try {
089 return doApply(descriptor);
090 }
091 catch (IllegalArgumentException e) {
092 return false;
093 }
094 }
095
096 private boolean doApply(@NotNull FunctionDescriptor descriptor) {
097 List<Name> nameParts = DescriptorUtils.getFqName(descriptor).pathSegments();
098 if (nameParts.size() != checkersWithPrefixChecker.size()) {
099 return false;
100 }
101 return allNamePartsValid(nameParts);
102 }
103
104 private boolean allNamePartsValid(@NotNull List<Name> nameParts) {
105 for (int i = 0; i < nameParts.size(); ++i) {
106 Name namePart = nameParts.get(i);
107 NamePredicate correspondingPredicate = checkersWithPrefixChecker.get(i);
108 if (!correspondingPredicate.apply(namePart)) {
109 return false;
110 }
111 }
112 return true;
113 }
114 };
115 }
116
117 @NotNull
118 public static DescriptorPredicate pattern(@NotNull NamePredicate... checkers) {
119 return pattern(Arrays.asList(checkers));
120 }
121
122 @NotNull
123 public static DescriptorPredicateImpl pattern(@NotNull String... names) {
124 return new DescriptorPredicateImpl(names);
125 }
126
127 public static class DescriptorPredicateImpl implements DescriptorPredicate {
128 private final String[] names;
129
130 private boolean receiverParameterExists;
131
132 private boolean checkOverridden;
133
134 public DescriptorPredicateImpl(String... names) {
135 this.names = names;
136 }
137
138 public DescriptorPredicateImpl receiverExists() {
139 this.receiverParameterExists = true;
140 return this;
141 }
142
143 public DescriptorPredicateImpl checkOverridden() {
144 this.checkOverridden = true;
145 return this;
146 }
147
148 private boolean matches(@NotNull CallableDescriptor callable) {
149 DeclarationDescriptor descriptor = callable;
150 int nameIndex = names.length - 1;
151 while (true) {
152 if (nameIndex == -1) {
153 return false;
154 }
155
156 if (!descriptor.getName().asString().equals(names[nameIndex])) {
157 return false;
158 }
159
160 nameIndex--;
161 descriptor = descriptor.getContainingDeclaration();
162 if (descriptor instanceof PackageFragmentDescriptor) {
163 return nameIndex == 0 && names[0].equals(((PackageFragmentDescriptor) descriptor).getFqName().asString());
164 }
165 }
166 }
167
168 @Override
169 public boolean apply(@NotNull FunctionDescriptor functionDescriptor) {
170 if ((functionDescriptor.getReceiverParameter() == null) == receiverParameterExists) {
171 return false;
172 }
173
174 if (!(functionDescriptor.getContainingDeclaration() instanceof ClassDescriptor)) {
175 return matches(functionDescriptor);
176 }
177
178 for (CallableMemberDescriptor real : OverrideResolver.getOverriddenDeclarations(functionDescriptor)) {
179 if (matches(real)) {
180 return true;
181 }
182 }
183
184 if (checkOverridden) {
185 for (CallableDescriptor overridden : OverrideResolver.getAllOverriddenDescriptors(functionDescriptor)) {
186 if (matches(overridden)) {
187 return true;
188 }
189 }
190 }
191
192 return false;
193 }
194 }
195 }