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.CallableMemberDescriptor;
022    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
023    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
024    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
025    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
026    import org.jetbrains.jet.lang.resolve.name.Name;
027    import org.jetbrains.k2js.translate.context.Namer;
028    
029    import java.util.Arrays;
030    import java.util.List;
031    import java.util.Set;
032    
033    public final class PatternBuilder {
034    
035        @NotNull
036        private static final NamePredicate JET = new NamePredicate("jet");
037    
038        @NotNull
039        private static final Name KOTLIN_NAME = Name.identifier(Namer.KOTLIN_LOWER_NAME);
040    
041        private PatternBuilder() {
042        }
043    
044        @NotNull
045        public static DescriptorPredicate pattern(@NotNull NamePredicate checker, @NotNull String stringWithPattern) {
046            List<NamePredicate> checkers = Lists.newArrayList(checker);
047            checkers.addAll(parseStringAsCheckerList(stringWithPattern));
048            return pattern(checkers);
049        }
050    
051        @NotNull
052        public static DescriptorPredicate pattern(@NotNull String stringWithPattern, @NotNull NamePredicate checker) {
053            List<NamePredicate> checkers = Lists.newArrayList(parseStringAsCheckerList(stringWithPattern));
054            checkers.add(checker);
055            return pattern(checkers);
056        }
057    
058        @NotNull
059        public static DescriptorPredicate pattern(@NotNull String string) {
060            List<NamePredicate> checkers = parseStringAsCheckerList(string);
061            return pattern(checkers);
062        }
063    
064        @NotNull
065        private static List<NamePredicate> parseStringAsCheckerList(@NotNull String stringWithPattern) {
066            String[] subPatterns = stringWithPattern.split("\\.");
067            List<NamePredicate> checkers = Lists.newArrayList();
068            for (String subPattern : subPatterns) {
069                String[] validNames = subPattern.split("\\|");
070                checkers.add(new NamePredicate(validNames));
071            }
072            return checkers;
073        }
074    
075        @NotNull
076        private static DescriptorPredicate pattern(@NotNull List<NamePredicate> checkers) {
077            assert !checkers.isEmpty();
078            final List<NamePredicate> checkersWithPrefixChecker = Lists.newArrayList();
079            if (!checkers.get(0).apply(KOTLIN_NAME)) {
080                checkersWithPrefixChecker.add(JET);
081            }
082    
083            checkersWithPrefixChecker.addAll(checkers);
084    
085            assert checkersWithPrefixChecker.size() > 1;
086    
087            return new DescriptorPredicate() {
088                @Override
089                public boolean apply(@NotNull FunctionDescriptor descriptor) {
090                    //TODO: no need to wrap if we check beforehand
091                    try {
092                        return doApply(descriptor);
093                    }
094                    catch (IllegalArgumentException e) {
095                        return false;
096                    }
097                }
098    
099                private boolean doApply(@NotNull FunctionDescriptor descriptor) {
100                    List<Name> nameParts = DescriptorUtils.getFQName(descriptor).pathSegments();
101                    if (nameParts.size() != checkersWithPrefixChecker.size()) {
102                        return false;
103                    }
104                    return allNamePartsValid(nameParts);
105                }
106    
107                private boolean allNamePartsValid(@NotNull List<Name> nameParts) {
108                    for (int i = 0; i < nameParts.size(); ++i) {
109                        Name namePart = nameParts.get(i);
110                        NamePredicate correspondingPredicate = checkersWithPrefixChecker.get(i);
111                        if (!correspondingPredicate.apply(namePart)) {
112                            return false;
113                        }
114                    }
115                    return true;
116                }
117            };
118        }
119    
120        @NotNull
121        public static DescriptorPredicate pattern(@NotNull NamePredicate... checkers) {
122            return pattern(Arrays.asList(checkers));
123        }
124    
125        @NotNull
126        public static DescriptorPredicateImpl pattern(@NotNull String... names) {
127            return new DescriptorPredicateImpl(names);
128        }
129    
130        @NotNull
131        public static DescriptorPredicateImpl pattern(@NotNull String[] root, @NotNull  String... names) {
132            return new DescriptorPredicateImpl(names).root(root);
133        }
134    
135        private static boolean isRootNamespace(DeclarationDescriptor declarationDescriptor) {
136            return declarationDescriptor instanceof NamespaceDescriptor && DescriptorUtils.isRootNamespace((NamespaceDescriptor) declarationDescriptor);
137        }
138    
139        public static class DescriptorPredicateImpl implements DescriptorPredicate {
140            private final String[] names;
141    
142            private boolean receiverParameterExists;
143    
144            private String[] root;
145            private boolean checkOverridden;
146    
147            public DescriptorPredicateImpl(String... names) {
148                this.names = names;
149            }
150    
151            public DescriptorPredicateImpl receiverExists() {
152                this.receiverParameterExists = true;
153                return this;
154            }
155    
156            public DescriptorPredicateImpl root(String... root) {
157                this.root = root;
158                return this;
159            }
160    
161            public DescriptorPredicateImpl checkOverridden() {
162                this.checkOverridden = true;
163                return this;
164            }
165    
166            private boolean check(FunctionDescriptor functionDescriptor) {
167                DeclarationDescriptor descriptor = functionDescriptor.getContainingDeclaration();
168                String[] list;
169                int nameIndex;
170                if (root == null) {
171                    list = names;
172                    nameIndex = list.length - 2;
173                }
174                else {
175                    assert names.length == 1;
176                    list = root;
177                    nameIndex = list.length - 1;
178                }
179    
180                do {
181                    if (nameIndex == -1) {
182                        return isRootNamespace(descriptor);
183                    }
184                    else if (isRootNamespace(descriptor)) {
185                        return false;
186                    }
187    
188                    if (!descriptor.getName().asString().equals(list[nameIndex--])) {
189                        return false;
190                    }
191                }
192                while ((descriptor = descriptor.getContainingDeclaration()) != null);
193                return false;
194            }
195    
196            @Override
197            public boolean apply(@NotNull FunctionDescriptor functionDescriptor) {
198                if ((functionDescriptor.getReceiverParameter() == null) == receiverParameterExists) {
199                    return false;
200                }
201    
202                // avoid unwrap FAKE_OVERRIDE
203                int nameIndex = names.length - 1;
204                if (!functionDescriptor.getName().asString().equals(names[nameIndex--])) {
205                    return false;
206                }
207    
208                DeclarationDescriptor descriptor;
209                if (functionDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
210                    assert functionDescriptor.getOverriddenDescriptors().size() > 0;
211                    descriptor = functionDescriptor.getOverriddenDescriptors().iterator().next();
212                }
213                else {
214                    descriptor = functionDescriptor;
215                }
216    
217                String[] list = names;
218                while ((descriptor = descriptor.getContainingDeclaration()) != null) {
219                    if (nameIndex == -1) {
220                        if (isRootNamespace(descriptor)) {
221                            return list == root || root == null;
222                        }
223                        else if (root == null) {
224                            return false;
225                        }
226                        else {
227                            nameIndex = root.length - 1;
228                            list = root;
229                        }
230                    }
231                    else if (isRootNamespace(descriptor)) {
232                        return false;
233                    }
234    
235                    if (!descriptor.getName().asString().equals(list[nameIndex--])) {
236                        // we check overridden on any mismatch - we can have classes with equal name from different packages
237                        return checkOverridden && checkOverridden(functionDescriptor);
238    
239                    }
240                }
241                return false;
242            }
243    
244            private boolean checkOverridden(FunctionDescriptor functionDescriptor) {
245                Set<? extends FunctionDescriptor> overriddenDescriptors = functionDescriptor.getOverriddenDescriptors();
246                if (overriddenDescriptors.isEmpty()) {
247                    return false;
248                }
249    
250                for (FunctionDescriptor overridden : overriddenDescriptors) {
251                    if (overridden.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
252                        for (FunctionDescriptor realOverridden : overridden.getOverriddenDescriptors()) {
253                            if (check(realOverridden) || checkOverridden(realOverridden)) {
254                                return true;
255                            }
256                        }
257                    }
258                    else if (check(overridden) || checkOverridden(overridden)) {
259                        return true;
260                    }
261                }
262                return false;
263            }
264        }
265    }