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