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