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.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.asm4.ClassReader;
028    import org.jetbrains.asm4.ClassVisitor;
029    import org.jetbrains.asm4.FieldVisitor;
030    import org.jetbrains.asm4.MethodVisitor;
031    import org.jetbrains.jet.codegen.ClassBuilder;
032    import org.jetbrains.jet.lang.psi.JetFile;
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(@NotNull Stack<StubElement> parentStack) {
057            this.parentStack = parentStack;
058            this.parent = parentStack.peek();
059        }
060    
061        @NotNull
062        @Override
063        public ClassVisitor getVisitor() {
064            assert v != null : "Called before class is defined";
065            return v;
066        }
067    
068        @Override
069        public void defineClass(
070                PsiElement origin,
071                int version,
072                int access,
073                @NotNull String name,
074                @Nullable String signature,
075                @NotNull String superName,
076                @NotNull String[] interfaces
077        ) {
078            assert v == null : "defineClass() called twice?";
079            v = new StubBuildingVisitor<Object>(null, EMPTY_STRATEGY, parent, access);
080    
081            super.defineClass(origin, version, access, name, signature, superName, interfaces);
082    
083            if (origin instanceof JetFile) {
084                FqName packageName = JetPsiUtil.getFQName((JetFile) origin);
085                String packageClassName = PackageClassUtils.getPackageClassName(packageName);
086    
087                if (name.equals(packageClassName) || name.endsWith("/" + packageClassName)) {
088                    isNamespace = true;
089                }
090            }
091    
092            if (!isNamespace) {
093                parentStack.push(v.getResult());
094            }
095    
096            ((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
097        }
098    
099        @Override
100        public MethodVisitor newMethod(
101                @Nullable PsiElement 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 != null) {
111                // If stub for method generated
112                markLastChild(origin);
113            }
114    
115            return internalVisitor;
116        }
117    
118        @Override
119        public FieldVisitor newField(
120                @Nullable PsiElement origin,
121                int access,
122                @NotNull String name,
123                @NotNull String desc,
124                @Nullable String signature,
125                @Nullable Object value
126        ) {
127            FieldVisitor internalVisitor = super.newField(origin, access, name, desc, signature, value);
128    
129            if (internalVisitor != null) {
130                // If stub for field generated
131                markLastChild(origin);
132            }
133    
134            return internalVisitor;
135        }
136    
137        private void markLastChild(@Nullable PsiElement origin) {
138            List children = v.getResult().getChildrenStubs();
139            StubBase last = (StubBase) children.get(children.size() - 1);
140    
141            PsiElement oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT);
142            if (oldOrigin != null) {
143                throw new IllegalStateException("Rewriting origin element: " + oldOrigin.getText() + " for stub " + last.toString());
144            }
145    
146            last.putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
147        }
148    
149        @Override
150        public void done() {
151            if (!isNamespace) {
152                StubElement pop = parentStack.pop();
153                assert pop == v.getResult();
154            }
155            super.done();
156        }
157    }