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, null);
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        @NotNull
100        @Override
101        public MethodVisitor newMethod(
102                @Nullable PsiElement origin,
103                int access,
104                @NotNull String name,
105                @NotNull String desc,
106                @Nullable String signature,
107                @Nullable String[] exceptions
108        ) {
109            MethodVisitor internalVisitor = super.newMethod(origin, access, name, desc, signature, exceptions);
110    
111            if (internalVisitor != EMPTY_METHOD_VISITOR) {
112                // If stub for method generated
113                markLastChild(origin);
114            }
115    
116            return internalVisitor;
117        }
118    
119        @NotNull
120        @Override
121        public FieldVisitor newField(
122                @Nullable PsiElement origin,
123                int access,
124                @NotNull String name,
125                @NotNull String desc,
126                @Nullable String signature,
127                @Nullable Object value
128        ) {
129            FieldVisitor internalVisitor = super.newField(origin, access, name, desc, signature, value);
130    
131            if (internalVisitor != EMPTY_FIELD_VISITOR) {
132                // If stub for field generated
133                markLastChild(origin);
134            }
135    
136            return internalVisitor;
137        }
138    
139        private void markLastChild(@Nullable PsiElement origin) {
140            List children = v.getResult().getChildrenStubs();
141            StubBase last = (StubBase) children.get(children.size() - 1);
142    
143            PsiElement oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT);
144            if (oldOrigin != null) {
145                throw new IllegalStateException("Rewriting origin element: " + oldOrigin.getText() + " for stub " + last.toString());
146            }
147    
148            last.putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
149        }
150    
151        @Override
152        public void done() {
153            if (!isNamespace) {
154                StubElement pop = parentStack.pop();
155                assert pop == v.getResult();
156            }
157            super.done();
158        }
159    }