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            private Builder constructor() {
066                assert currentFQName != null;
067                assert currentObjectName != null;
068                declareInner(currentFQName, "<init>", currentObjectName);
069                return this;
070            }
071    
072            @NotNull
073            public Builder methods(@NotNull String... methodNames) {
074                assert currentFQName != null;
075                declareMethods(currentFQName, methodNames);
076                return this;
077            }
078    
079            @NotNull
080            public Builder properties(@NotNull String... propertyNames) {
081                assert currentFQName != null;
082                declareReadonlyProperties(currentFQName, propertyNames);
083                return this;
084            }
085        }
086    
087        @NotNull
088        public static StandardClasses bindImplementations(@NotNull JsScope kotlinObjectScope) {
089            StandardClasses standardClasses = new StandardClasses(kotlinObjectScope);
090            declareJetObjects(standardClasses);
091            return standardClasses;
092        }
093    
094        private static void declareJetObjects(@NotNull StandardClasses standardClasses) {
095            standardClasses.declare().forFQ("jet.Iterator").kotlinClass("Iterator").methods("next").properties("hasNext");
096    
097            standardClasses.declare().forFQ("jet.IntRange").kotlinClass("NumberRange")
098                    .methods("iterator", "contains").properties("start", "end", "increment");
099    
100            standardClasses.declare().forFQ("jet.IntProgression").kotlinClass("NumberProgression")
101                    .methods("iterator", "contains").properties("start", "end", "increment");
102    
103            standardClasses.declare().forFQ("jet.Enum").kotlinClass("Enum");
104        }
105    
106    
107        @NotNull
108        private final JsScope kotlinScope;
109    
110    
111        @NotNull
112        private final Map<FqNameUnsafe, JsName> standardObjects = Maps.newHashMap();
113    
114        @NotNull
115        private final Map<FqNameUnsafe, JsScope> scopeMap = Maps.newHashMap();
116    
117        private StandardClasses(@NotNull JsScope kotlinScope) {
118            this.kotlinScope = kotlinScope;
119        }
120    
121        private void declareTopLevelObjectInScope(@NotNull JsScope scope, @NotNull Map<FqNameUnsafe, JsName> map,
122                                                  @NotNull FqNameUnsafe fullQualifiedName, @NotNull String name) {
123            JsName declaredName = scope.declareName(name);
124            map.put(fullQualifiedName, declaredName);
125            scopeMap.put(fullQualifiedName, new JsScope(scope, "scope for " + name));
126        }
127    
128        private void declareKotlinObject(@NotNull FqNameUnsafe fullQualifiedName, @NotNull String kotlinLibName) {
129            declareTopLevelObjectInScope(kotlinScope, standardObjects, fullQualifiedName, kotlinLibName);
130        }
131    
132        private void declareInner(@NotNull FqNameUnsafe fullQualifiedClassName,
133                                  @NotNull String shortMethodName,
134                                  @NotNull String javascriptName) {
135            JsScope classScope = scopeMap.get(fullQualifiedClassName);
136            assert classScope != null;
137            FqNameUnsafe fullQualifiedMethodName = fullQualifiedClassName.child(Name.guess(shortMethodName));
138            standardObjects.put(fullQualifiedMethodName, classScope.declareName(javascriptName));
139        }
140    
141        private void declareMethods(@NotNull FqNameUnsafe classFQName,
142                                    @NotNull String... methodNames) {
143            for (String methodName : methodNames) {
144                declareInner(classFQName, methodName, methodName);
145            }
146        }
147    
148        private void declareReadonlyProperties(@NotNull FqNameUnsafe classFQName,
149                                               @NotNull String... propertyNames) {
150            for (String propertyName : propertyNames) {
151                declareInner(classFQName, propertyName, propertyName);
152            }
153        }
154    
155        public boolean isStandardObject(@NotNull DeclarationDescriptor descriptor) {
156            return standardObjects.containsKey(getFQName(descriptor));
157        }
158    
159        @NotNull
160        public JsName getStandardObjectName(@NotNull DeclarationDescriptor descriptor) {
161            return standardObjects.get(getFQName(descriptor));
162        }
163    
164        @NotNull
165        private Builder declare() {
166            return new Builder();
167        }
168    }