~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to lexer/src/org/netbeans/spi/lexer/TokenFactory.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
 
 
42
package org.netbeans.spi.lexer;
 
43
 
 
44
import java.util.Set;
 
45
import org.netbeans.api.lexer.PartType;
 
46
import org.netbeans.api.lexer.Token;
 
47
import org.netbeans.api.lexer.TokenId;
 
48
import org.netbeans.lib.editor.util.CharSequenceUtilities;
 
49
import org.netbeans.lib.lexer.LanguageOperation;
 
50
import org.netbeans.lib.lexer.LexerInputOperation;
 
51
import org.netbeans.lib.lexer.TokenIdImpl;
 
52
import org.netbeans.lib.lexer.token.CustomTextToken;
 
53
import org.netbeans.lib.lexer.token.DefaultToken;
 
54
import org.netbeans.lib.lexer.token.ComplexToken;
 
55
import org.netbeans.lib.lexer.token.ComplexToken;
 
56
import org.netbeans.lib.lexer.token.PropertyToken;
 
57
import org.netbeans.lib.lexer.token.TextToken;
 
58
 
 
59
/**
 
60
 * Lexer should delegate all the token instances creation to this class.
 
61
 * <br/>
 
62
 * It's not allowed to create empty tokens.
 
63
 *
 
64
 * @author Miloslav Metelka
 
65
 * @version 1.00
 
66
 */
 
67
 
 
68
public final class TokenFactory<T extends TokenId> {
 
69
 
 
70
    /** Flag for additional correctness checks (may degrade performance). */
 
71
    private static final boolean testing = Boolean.getBoolean("netbeans.debug.lexer.test");
 
72
 
 
73
    /**
 
74
     * Token instance that should be returned by the lexer
 
75
     * if there is an active filtering of certain token ids
 
76
     * and the just recognized token-id should be skipped.
 
77
     */
 
78
    public static final Token SKIP_TOKEN
 
79
        = new TextToken<TokenId>(
 
80
            new TokenIdImpl("skip-token-id; special id of TokenFactory.SKIP_TOKEN; " + // NOI18N
 
81
                    " It should never be part of token sequence", 0, null), // NOI18N
 
82
            "" // empty skip token text NOI18N
 
83
        );
 
84
    
 
85
    private final LexerInputOperation<T> operation;
 
86
    
 
87
    TokenFactory(LexerInputOperation<T> operation) {
 
88
        this.operation = operation;
 
89
    }
 
90
 
 
91
    /**
 
92
     * Create token with token length corresponding
 
93
     * to the number of characters read from the lexer input.
 
94
     *
 
95
     * @see #createToken(TokenId, int)
 
96
     */
 
97
    public Token<T> createToken(T id) {
 
98
        return createToken(id, operation.readIndex());
 
99
    }
 
100
 
 
101
    /**
 
102
     * Create regular token instance with an explicit length.
 
103
     *
 
104
     * @param id non-null token id recognized by the lexer.
 
105
     * @param length >=0 length of the token to be created. The length must not
 
106
     *  exceed the number of characters read from the lexer input.
 
107
     * @return non-null regular token instance.
 
108
     *  <br/>
 
109
     *  {@link #SKIP_TOKEN} will be returned
 
110
     *  if tokens for the given token id should be skipped
 
111
     *  because of token id filter.
 
112
     */
 
113
    public Token<T> createToken(T id, int length) {
 
114
        if (isSkipToken(id)) {
 
115
            operation.tokenRecognized(length, true);
 
116
            return skipToken();
 
117
        } else { // Do not skip the token
 
118
            if (operation.tokenRecognized(length, false)) { // Create preprocessed token
 
119
//                return new PreprocessedTextToken<T>(id, operation.tokenLength());
 
120
                return new DefaultToken<T>(id, operation.tokenLength());
 
121
            } else {
 
122
                return new DefaultToken<T>(id, operation.tokenLength());
 
123
            }
 
124
        }
 
125
    }
 
126
 
 
127
    /**
 
128
     * Create regular token instance with an explicit length and part type.
 
129
     *
 
130
     * @param id non-null token id recognized by the lexer.
 
131
     * @param length >=0 length of the token to be created. The length must not
 
132
     *  exceed the number of characters read from the lexer input.
 
133
     * @param partType whether this token is complete token or a part of a complete token.
 
134
     * @return non-null regular token instance.
 
135
     *  <br/>
 
136
     *  {@link #SKIP_TOKEN} will be returned
 
137
     *  if tokens for the given token id should be skipped
 
138
     *  because of token id filter.
 
139
     */
 
140
    public Token<T> createToken(T id, int length, PartType partType) {
 
141
        checkPartTypeNonNull(partType);
 
142
        if (partType == PartType.COMPLETE)
 
143
            return createToken(id, length);
 
144
 
 
145
        if (isSkipToken(id)) {
 
146
            operation.tokenRecognized(length, true);
 
147
            return skipToken();
 
148
        } else { // Do not skip the token
 
149
            if (operation.tokenRecognized(length, false)) { // Create preprocessed token
 
150
//                return new ComplexToken<T>(id, operation.tokenLength(), null, partType, null);
 
151
                return new PropertyToken<T>(id, operation.tokenLength(), null, partType);
 
152
            } else {
 
153
                return new PropertyToken<T>(id, operation.tokenLength(), null, partType);
 
154
            }
 
155
        }
 
156
    }
 
157
 
 
158
    /**
 
159
     * Get flyweight token for the given arguments.
 
160
     * <br/>
 
161
     * <b>Note:</b> The returned token will not be flyweight under certain
 
162
     * conditions - see return value description.
 
163
     *
 
164
     * @param id non-null token id.
 
165
     * @param text non-null text that the flyweight token should carry.
 
166
     * @return non-null flyweight token instance.
 
167
     *  <br/>
 
168
     *  For performance reasons there is a limit for number of successive
 
169
     *  flyweight tokens. If this limit would be exceeded a single non-flyweight
 
170
     *  token gets created instead of flyweight one.
 
171
     *  <br/>
 
172
     *  {@link #SKIP_TOKEN} will be returned
 
173
     *  if tokens for the given token id should be skipped
 
174
     *  because of token id filter.
 
175
     */
 
176
    public Token<T> getFlyweightToken(T id, String text) {
 
177
        assert (text.length() <= operation.readIndex());
 
178
        // Compare each recognized char with the corresponding char in text
 
179
        if (testing) {
 
180
            for (int i = 0; i < text.length(); i++) {
 
181
                if (text.charAt(i) != operation.readExisting(i)) {
 
182
                    throw new IllegalArgumentException("Flyweight text in " + // NOI18N
 
183
                            "TokenFactory.getFlyweightToken(" + id + ", \"" + // NOI18N
 
184
                            CharSequenceUtilities.debugText(text) + "\") " + // NOI18N
 
185
                            "differs from recognized text: '" + // NOI18N
 
186
                            CharSequenceUtilities.debugChar(operation.readExisting(i)) +
 
187
                            "' != '" + CharSequenceUtilities.debugChar(text.charAt(i)) + // NOI18N
 
188
                            "' at index=" + i // NOI18N
 
189
                    );
 
190
                }
 
191
            }
 
192
        }
 
193
 
 
194
        // Check whether token with given id should be created
 
195
        if (isSkipToken(id)) {
 
196
            operation.tokenRecognized(text.length(), true);
 
197
            return skipToken();
 
198
        } else { // Do not skip the token
 
199
            if (operation.tokenRecognized(text.length(), false)) { // Create preprocessed token
 
200
//                return new PreprocessedTextToken<T>(id, operation.tokenLength());
 
201
                return new DefaultToken<T>(id, operation.tokenLength());
 
202
            } else if (operation.isFlyTokenAllowed()) {
 
203
                LanguageOperation<T> langOp = operation.languageOperation();
 
204
                return langOp.getFlyweightToken(id, text);
 
205
            } else { // return non-flyweight token
 
206
                return new DefaultToken<T>(id, operation.tokenLength());
 
207
            }
 
208
        }
 
209
    }
 
210
    
 
211
    /**
 
212
     * Create token with properties.
 
213
     *
 
214
     * @param id non-null token id.
 
215
     * @param length >=0 length of the token to be created. The length must not
 
216
     *  exceed the number of characters read from the lexer input.
 
217
     * @param propertyProvider non-null token property provider.
 
218
     * @param partType whether this token is complete or just a part of complete token.
 
219
     *  See {@link TokenPropertyProvider} for examples how this parameter may be used.
 
220
     * @return non-null property token instance.
 
221
     *  <br/>
 
222
     *  {@link #SKIP_TOKEN} will be returned
 
223
     *  if tokens for the given token id should be skipped
 
224
     *  because of token id filter.
 
225
     */
 
226
    public Token<T> createPropertyToken(T id, int length,
 
227
    TokenPropertyProvider propertyProvider, PartType partType) {
 
228
        checkPartTypeNonNull(partType);
 
229
        if (isSkipToken(id)) {
 
230
            operation.tokenRecognized(length, true);
 
231
            return skipToken();
 
232
        } else { // Do not skip the token
 
233
            if (operation.tokenRecognized(length, false)) { // Create preprocessed token
 
234
//                return new ComplexToken<T>(id, operation.tokenLength(),
 
235
//                    propertyProvider, null, partType);
 
236
                return new PropertyToken<T>(id, operation.tokenLength(),
 
237
                    propertyProvider, partType);
 
238
            } else {
 
239
                return new PropertyToken<T>(id, operation.tokenLength(),
 
240
                    propertyProvider, partType);
 
241
            }
 
242
        }
 
243
    }
 
244
 
 
245
    /**
 
246
     * Create token with a custom text that possibly differs from the text
 
247
     * represented by the token in the input text.
 
248
     */
 
249
    public Token<T> createCustomTextToken(T id, CharSequence text, int length, PartType partType) {
 
250
        checkPartTypeNonNull(partType);
 
251
        if (isSkipToken(id)) {
 
252
            operation.tokenRecognized(length, true);
 
253
            return skipToken();
 
254
        } else { // Do not skip the token
 
255
            if (operation.tokenRecognized(length, false)) { // Create preprocessed token
 
256
                return new CustomTextToken<T>(id, operation.tokenLength(), text, partType);
 
257
//                return new ComplexToken<T>(id, operation.tokenLength(), null, text, partType);
 
258
            } else {
 
259
                return new CustomTextToken<T>(id, operation.tokenLength(), text, partType);
 
260
            }
 
261
        }
 
262
    }
 
263
    
 
264
    private boolean isSkipToken(T id) {
 
265
        Set<? extends TokenId> skipTokenIds = operation.skipTokenIds();
 
266
        return (skipTokenIds != null) && skipTokenIds.contains(id);
 
267
    }
 
268
 
 
269
    @SuppressWarnings("unchecked") // NOI18N
 
270
    private Token<T> skipToken() {
 
271
        return SKIP_TOKEN;
 
272
    }
 
273
    
 
274
    private void checkPartTypeNonNull(PartType partType) {
 
275
        if (partType == null)
 
276
            throw new IllegalArgumentException("partType must be non-null");
 
277
    }
 
278
    
 
279
}