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.jet.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.Nullable;
026    import org.jetbrains.asm4.ClassReader;
027    import org.jetbrains.asm4.ClassVisitor;
028    import org.jetbrains.asm4.FieldVisitor;
029    import org.jetbrains.asm4.MethodVisitor;
030    import org.jetbrains.jet.codegen.ClassBuilder;
031    import org.jetbrains.jet.lang.psi.JetFile;
032    import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
033    import org.jetbrains.jet.lang.psi.JetPsiUtil;
034    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
035    import org.jetbrains.jet.lang.resolve.name.FqName;
036    
037    import java.util.List;
038    
039    public 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    }