1/*
2 * Copyright 2012, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.smalidea;
33
34import com.intellij.lang.PsiBuilder;
35import com.intellij.lang.PsiBuilder.Marker;
36import com.intellij.psi.TokenType;
37import com.intellij.psi.tree.IElementType;
38import org.antlr.runtime.CommonToken;
39import org.antlr.runtime.Token;
40import org.antlr.runtime.TokenSource;
41import org.antlr.runtime.TokenStream;
42import org.jetbrains.annotations.Nullable;
43import org.jf.smali.InvalidToken;
44import org.jf.smali.smaliParser;
45
46import javax.annotation.Nonnull;
47import java.util.ArrayList;
48
49public class PsiBuilderTokenStream implements TokenStream {
50    @Nonnull private PsiBuilder psiBuilder;
51    @Nullable private CommonToken currentToken = null;
52    @Nonnull private ArrayList<Marker> markers = new ArrayList<PsiBuilder.Marker>();
53
54    public PsiBuilderTokenStream(@Nonnull PsiBuilder psiBuilder) {
55        this.psiBuilder = psiBuilder;
56    }
57
58    @Override public Token LT(int k) {
59        if (k == 1) {
60            if (currentToken == null) {
61                buildCurrentToken();
62            }
63            return currentToken;
64        }
65        throw new UnsupportedOperationException();
66    }
67
68    @Override public int range() {
69        return currentToken==null?0:1;
70    }
71
72    @Override public Token get(int i) {
73        throw new UnsupportedOperationException();
74    }
75
76    @Override public TokenSource getTokenSource() {
77        throw new UnsupportedOperationException();
78    }
79
80    @Override public String toString(int start, int stop) {
81        throw new UnsupportedOperationException();
82    }
83
84    @Override public String toString(Token start, Token stop) {
85        throw new UnsupportedOperationException();
86    }
87
88    @Override public void consume() {
89        psiBuilder.advanceLexer();
90        buildCurrentToken();
91    }
92
93    private void buildCurrentToken() {
94        IElementType element = psiBuilder.getTokenType();
95        if (element != null) {
96            if (element instanceof SmaliLexicalElementType) {
97                SmaliLexicalElementType elementType = (SmaliLexicalElementType)element;
98                currentToken = new CommonToken(elementType.tokenId, psiBuilder.getTokenText());
99            } else if (element == TokenType.BAD_CHARACTER) {
100                currentToken = new InvalidToken("", psiBuilder.getTokenText());
101            } else {
102                throw new UnsupportedOperationException();
103            }
104        } else {
105            currentToken = new CommonToken(Token.EOF);
106        }
107    }
108
109    @Override public int LA(int i) {
110        IElementType elementType = psiBuilder.lookAhead(i-1);
111        if (elementType == null) {
112            return -1;
113        } else if (elementType instanceof SmaliLexicalElementType) {
114            return ((SmaliLexicalElementType)elementType).tokenId;
115        } else if (elementType == TokenType.BAD_CHARACTER) {
116            return smaliParser.INVALID_TOKEN;
117        }
118        throw new UnsupportedOperationException();
119    }
120
121    @Override public int mark() {
122        int ret = markers.size();
123        markers.add(psiBuilder.mark());
124        return ret;
125    }
126
127    @Override public int index() {
128        return psiBuilder.getCurrentOffset();
129    }
130
131    @Override public void rewind(int markerIndex) {
132        PsiBuilder.Marker marker = markers.get(markerIndex);
133        marker.rollbackTo();
134        while (markerIndex < markers.size()) {
135            markers.remove(markerIndex);
136        }
137    }
138
139    @Override public void rewind() {
140        rewind(markers.size()-1);
141        mark();
142    }
143
144    @Override public void release(int markerIndex) {
145        while (markerIndex < markers.size()) {
146            markers.remove(markerIndex).drop();
147        }
148    }
149
150    @Override public void seek(int index) {
151        if (index < psiBuilder.getCurrentOffset()) {
152            throw new UnsupportedOperationException();
153        }
154        while (index > psiBuilder.getCurrentOffset()) {
155            consume();
156        }
157    }
158
159    @Override public int size() {
160        throw new UnsupportedOperationException();
161    }
162
163    @Override public String getSourceName() {
164        return null;
165    }
166}
167