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.context;
018    
019    import com.google.common.collect.Maps;
020    import com.google.dart.compiler.backend.js.ast.JsName;
021    import com.google.dart.compiler.backend.js.ast.JsObjectScope;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
025    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
026    import org.jetbrains.jet.lang.resolve.name.Name;
027    
028    import java.util.Map;
029    
030    import static com.google.dart.compiler.backend.js.ast.AstPackage.JsObjectScope;
031    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName;
032    
033    /**
034     * Provides a mechanism to bind some of the kotlin/java declations with library implementations.
035     * Makes sense only for those declaration that cannot be annotated. (Use library annotation in this case)
036     */
037    public final class StandardClasses {
038    
039        private final class Builder {
040    
041            @Nullable
042            private /*var*/ FqNameUnsafe currentFQName = null;
043            @Nullable
044            private /*var*/ String currentObjectName = null;
045    
046            @NotNull
047            public Builder forFQ(@NotNull String classFQName) {
048                currentFQName = new FqNameUnsafe(classFQName);
049                return this;
050            }
051    
052            @NotNull
053            public Builder kotlinClass(@NotNull String kotlinName) {
054                kotlinTopLevelObject(kotlinName);
055                constructor();
056                return this;
057            }
058    
059            private void kotlinTopLevelObject(@NotNull String kotlinName) {
060                assert currentFQName != null;
061                currentObjectName = kotlinName;
062                declareKotlinObject(currentFQName, kotlinName);
063            }
064    
065            @NotNull
066            private Builder constructor() {
067                assert currentFQName != null;
068                assert currentObjectName != null;
069                declareInner(currentFQName, "<init>", currentObjectName);
070                return this;
071            }
072    
073            @NotNull
074            public Builder methods(@NotNull String... methodNames) {
075                assert currentFQName != null;
076                declareMethods(currentFQName, methodNames);
077                return this;
078            }
079    
080            @NotNull
081            public Builder properties(@NotNull String... propertyNames) {
082                assert currentFQName != null;
083                declareReadonlyProperties(currentFQName, propertyNames);
084                return this;
085            }
086        }
087    
088        @NotNull
089        public static StandardClasses bindImplementations(@NotNull JsObjectScope kotlinObjectScope) {
090            StandardClasses standardClasses = new StandardClasses(kotlinObjectScope);
091            declareKotlinStandardClasses(standardClasses);
092            return standardClasses;
093        }
094    
095        private static void declareKotlinStandardClasses(@NotNull StandardClasses standardClasses) {
096            standardClasses.declare().forFQ("kotlin.Iterator").kotlinClass("Iterator").methods("next").properties("hasNext");
097    
098            standardClasses.declare().forFQ("kotlin.IntRange").kotlinClass("NumberRange")
099                    .methods("iterator", "contains").properties("start", "end", "increment");
100    
101            standardClasses.declare().forFQ("kotlin.LongRange").kotlinClass("LongRange")
102                    .methods("iterator", "contains").properties("start", "end", "increment");
103    
104            standardClasses.declare().forFQ("kotlin.CharRange").kotlinClass("CharRange")
105                    .methods("iterator", "contains").properties("start", "end", "increment");
106    
107            standardClasses.declare().forFQ("kotlin.IntProgression").kotlinClass("NumberProgression")
108                    .methods("iterator", "contains").properties("start", "end", "increment");
109    
110            standardClasses.declare().forFQ("kotlin.LongProgression").kotlinClass("LongProgression")
111                    .methods("iterator", "contains").properties("start", "end", "increment");
112    
113            standardClasses.declare().forFQ("kotlin.CharProgression").kotlinClass("CharProgression")
114                    .methods("iterator", "contains").properties("start", "end", "increment");
115    
116            standardClasses.declare().forFQ("kotlin.Enum").kotlinClass("Enum");
117    
118            standardClasses.declare().forFQ("kotlin.Comparable").kotlinClass("Comparable");
119        }
120    
121    
122        @NotNull
123        private final JsObjectScope kotlinScope;
124    
125    
126        @NotNull
127        private final Map<FqNameUnsafe, JsName> standardObjects = Maps.newHashMap();
128    
129        @NotNull
130        private final Map<FqNameUnsafe, JsObjectScope> scopeMap = Maps.newHashMap();
131    
132        private StandardClasses(@NotNull JsObjectScope kotlinScope) {
133            this.kotlinScope = kotlinScope;
134        }
135    
136        private void declareTopLevelObjectInScope(@NotNull JsObjectScope scope, @NotNull Map<FqNameUnsafe, JsName> map,
137                                                  @NotNull FqNameUnsafe fullQualifiedName, @NotNull String name) {
138            JsName declaredName = scope.declareName(name);
139            map.put(fullQualifiedName, declaredName);
140            scopeMap.put(fullQualifiedName, JsObjectScope(scope, "scope for " + name));
141        }
142    
143        private void declareKotlinObject(@NotNull FqNameUnsafe fullQualifiedName, @NotNull String kotlinLibName) {
144            declareTopLevelObjectInScope(kotlinScope, standardObjects, fullQualifiedName, kotlinLibName);
145        }
146    
147        private void declareInner(@NotNull FqNameUnsafe fullQualifiedClassName,
148                                  @NotNull String shortMethodName,
149                                  @NotNull String javascriptName) {
150            JsObjectScope classScope = scopeMap.get(fullQualifiedClassName);
151            assert classScope != null;
152            FqNameUnsafe fullQualifiedMethodName = fullQualifiedClassName.child(Name.guess(shortMethodName));
153            standardObjects.put(fullQualifiedMethodName, classScope.declareName(javascriptName));
154        }
155    
156        private void declareMethods(@NotNull FqNameUnsafe classFQName,
157                                    @NotNull String... methodNames) {
158            for (String methodName : methodNames) {
159                declareInner(classFQName, methodName, methodName);
160            }
161        }
162    
163        private void declareReadonlyProperties(@NotNull FqNameUnsafe classFQName,
164                                               @NotNull String... propertyNames) {
165            for (String propertyName : propertyNames) {
166                declareInner(classFQName, propertyName, propertyName);
167            }
168        }
169    
170        public boolean isStandardObject(@NotNull DeclarationDescriptor descriptor) {
171            return standardObjects.containsKey(getFqName(descriptor));
172        }
173    
174        @NotNull
175        public JsName getStandardObjectName(@NotNull DeclarationDescriptor descriptor) {
176            return standardObjects.get(getFqName(descriptor));
177        }
178    
179        @NotNull
180        private Builder declare() {
181            return new Builder();
182        }
183    }