001    /*
002     * Copyright 2010-2015 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.kotlin.asJava;
018    
019    import com.intellij.psi.PsiElement;
020    import com.intellij.psi.impl.compiled.InnerClassSourceStrategy;
021    import com.intellij.psi.impl.compiled.StubBuildingVisitor;
022    import com.intellij.psi.stubs.StubBase;
023    import com.intellij.psi.stubs.StubElement;
024    import com.intellij.util.containers.Stack;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.kotlin.codegen.AbstractClassBuilder;
028    import org.jetbrains.kotlin.load.kotlin.PackageClassUtils;
029    import org.jetbrains.kotlin.name.FqName;
030    import org.jetbrains.kotlin.psi.JetFile;
031    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
032    import org.jetbrains.org.objectweb.asm.ClassVisitor;
033    import org.jetbrains.org.objectweb.asm.FieldVisitor;
034    import org.jetbrains.org.objectweb.asm.MethodVisitor;
035    
036    import java.util.List;
037    
038    public class StubClassBuilder extends AbstractClassBuilder {
039        private static final InnerClassSourceStrategy<Object> EMPTY_STRATEGY = new InnerClassSourceStrategy<Object>() {
040            @Override
041            public Object findInnerClass(String s, Object o) {
042                return null;
043            }
044    
045            @Override
046            public void accept(Object innerClass, StubBuildingVisitor<Object> visitor) {
047                throw new UnsupportedOperationException("Shall not be called!");
048            }
049        };
050        private final StubElement parent;
051        private StubBuildingVisitor v;
052        private final Stack<StubElement> parentStack;
053        private boolean isPackageClass = false;
054    
055        public StubClassBuilder(@NotNull Stack<StubElement> parentStack) {
056            this.parentStack = parentStack;
057            this.parent = parentStack.peek();
058        }
059    
060        @NotNull
061        @Override
062        public ClassVisitor getVisitor() {
063            assert v != null : "Called before class is defined";
064            return v;
065        }
066    
067        @Override
068        public void defineClass(
069                PsiElement origin,
070                int version,
071                int access,
072                @NotNull String name,
073                @Nullable String signature,
074                @NotNull String superName,
075                @NotNull String[] interfaces
076        ) {
077            assert v == null : "defineClass() called twice?";
078            v = new StubBuildingVisitor<Object>(null, EMPTY_STRATEGY, parent, access, null);
079    
080            super.defineClass(origin, version, access, name, signature, superName, interfaces);
081    
082            if (origin instanceof JetFile) {
083                FqName packageName = ((JetFile) origin).getPackageFqName();
084                String packageClassName = PackageClassUtils.getPackageClassName(packageName);
085    
086                if (name.equals(packageClassName) || name.endsWith("/" + packageClassName)) {
087                    isPackageClass = true;
088                }
089            }
090    
091            if (!isPackageClass) {
092                parentStack.push(v.getResult());
093            }
094    
095            ((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
096        }
097    
098        @NotNull
099        @Override
100        public MethodVisitor newMethod(
101                @NotNull JvmDeclarationOrigin origin,
102                int access,
103                @NotNull String name,
104                @NotNull String desc,
105                @Nullable String signature,
106                @Nullable String[] exceptions
107        ) {
108            MethodVisitor internalVisitor = super.newMethod(origin, access, name, desc, signature, exceptions);
109    
110            if (internalVisitor != EMPTY_METHOD_VISITOR) {
111                // If stub for method generated
112                markLastChild(origin.getElement());
113            }
114    
115            return internalVisitor;
116        }
117    
118        @NotNull
119        @Override
120        public FieldVisitor newField(
121                @NotNull JvmDeclarationOrigin origin,
122                int access,
123                @NotNull String name,
124                @NotNull String desc,
125                @Nullable String signature,
126                @Nullable Object value
127        ) {
128            FieldVisitor internalVisitor = super.newField(origin, access, name, desc, signature, value);
129    
130            if (internalVisitor != EMPTY_FIELD_VISITOR) {
131                // If stub for field generated
132                markLastChild(origin.getElement());
133            }
134    
135            return internalVisitor;
136        }
137    
138        private void markLastChild(@Nullable PsiElement origin) {
139            List children = v.getResult().getChildrenStubs();
140            StubBase last = (StubBase) children.get(children.size() - 1);
141    
142            PsiElement oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT);
143            if (oldOrigin != null) {
144                throw new IllegalStateException("Rewriting origin element: " + oldOrigin.getText() + " for stub " + last.toString());
145            }
146    
147            last.putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
148        }
149    
150        @Override
151        public void done() {
152            if (!isPackageClass) {
153                StubElement pop = parentStack.pop();
154                assert pop == v.getResult();
155            }
156            super.done();
157        }
158    }