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    }