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
017package org.jetbrains.jet.asJava;
018
019import com.intellij.psi.PsiElement;
020import com.intellij.psi.impl.compiled.InnerClassSourceStrategy;
021import com.intellij.psi.impl.compiled.StubBuildingVisitor;
022import com.intellij.psi.stubs.StubBase;
023import com.intellij.psi.stubs.StubElement;
024import com.intellij.util.containers.Stack;
025import org.jetbrains.annotations.Nullable;
026import org.jetbrains.asm4.ClassReader;
027import org.jetbrains.asm4.ClassVisitor;
028import org.jetbrains.asm4.FieldVisitor;
029import org.jetbrains.asm4.MethodVisitor;
030import org.jetbrains.jet.codegen.ClassBuilder;
031import org.jetbrains.jet.lang.psi.JetFile;
032import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
033import org.jetbrains.jet.lang.psi.JetPsiUtil;
034import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
035import org.jetbrains.jet.lang.resolve.name.FqName;
036
037import java.util.List;
038
039public class StubClassBuilder extends ClassBuilder {
040    private static final InnerClassSourceStrategy<Object> EMPTY_STRATEGY = new InnerClassSourceStrategy<Object>() {
041        @Override
042        public Object findInnerClass(String s, Object o) {
043            return null;
044        }
045
046        @Override
047        public ClassReader readerForInnerClass(Object o) {
048            throw new UnsupportedOperationException("Shall not be called!");
049        }
050    };
051    private final StubElement parent;
052    private StubBuildingVisitor v;
053    private final Stack<StubElement> parentStack;
054    private boolean isNamespace = false;
055
056    public StubClassBuilder(Stack<StubElement> parentStack) {
057        this.parentStack = parentStack;
058        this.parent = parentStack.peek();
059    }
060
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(PsiElement origin, int version, int access, String name, @Nullable String signature, String superName, String[] interfaces) {
069        assert v == null : "defineClass() called twice?";
070        v = new StubBuildingVisitor<Object>(null, EMPTY_STRATEGY, parent, access);
071
072        super.defineClass(origin, version, access, name, signature, superName, interfaces);
073
074        if (origin instanceof JetFile) {
075            FqName packageName = JetPsiUtil.getFQName((JetFile) origin);
076            String packageClassName = PackageClassUtils.getPackageClassName(packageName);
077
078            if (name.equals(packageClassName) || name.endsWith("/" + packageClassName)) {
079                isNamespace = true;
080            }
081        }
082
083        if (!isNamespace) {
084            parentStack.push(v.getResult());
085        }
086
087        ((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
088    }
089
090    @Override
091    public MethodVisitor newMethod(@Nullable PsiElement origin, int access, String name, String desc, @Nullable String signature, @Nullable String[] exceptions) {
092        MethodVisitor internalVisitor = super.newMethod(origin, access, name, desc, signature, exceptions);
093
094        if (internalVisitor != null) {
095            // If stub for method generated
096            markLastChild(origin);
097        }
098
099        return internalVisitor;
100    }
101
102    @Override
103    public FieldVisitor newField(@Nullable PsiElement origin, int access, String name, String desc, @Nullable String signature, @Nullable Object value) {
104        FieldVisitor internalVisitor = super.newField(origin, access, name, desc, signature, value);
105
106        if (internalVisitor != null) {
107            // If stub for field generated
108            markLastChild(origin);
109        }
110
111        return internalVisitor;
112    }
113
114    private void markLastChild(@Nullable PsiElement origin) {
115        List children = v.getResult().getChildrenStubs();
116        StubBase last = (StubBase) children.get(children.size() - 1);
117
118        PsiElement oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT);
119        if (oldOrigin != null) {
120            throw new IllegalStateException("Rewriting origin element: " + oldOrigin.getText() + " for stub " + last.toString());
121        }
122
123        last.putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
124    }
125
126    @Override
127    public void done() {
128        if (!isNamespace) {
129            StubElement pop = parentStack.pop();
130            assert pop == v.getResult();
131        }
132        super.done();
133    }
134}