2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
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]"
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.
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.
42
package org.netbeans.spi.lexer;
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;
60
* Lexer should delegate all the token instances creation to this class.
62
* It's not allowed to create empty tokens.
64
* @author Miloslav Metelka
68
public final class TokenFactory<T extends TokenId> {
70
/** Flag for additional correctness checks (may degrade performance). */
71
private static final boolean testing = Boolean.getBoolean("netbeans.debug.lexer.test");
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.
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
85
private final LexerInputOperation<T> operation;
87
TokenFactory(LexerInputOperation<T> operation) {
88
this.operation = operation;
92
* Create token with token length corresponding
93
* to the number of characters read from the lexer input.
95
* @see #createToken(TokenId, int)
97
public Token<T> createToken(T id) {
98
return createToken(id, operation.readIndex());
102
* Create regular token instance with an explicit length.
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.
109
* {@link #SKIP_TOKEN} will be returned
110
* if tokens for the given token id should be skipped
111
* because of token id filter.
113
public Token<T> createToken(T id, int length) {
114
if (isSkipToken(id)) {
115
operation.tokenRecognized(length, true);
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());
122
return new DefaultToken<T>(id, operation.tokenLength());
128
* Create regular token instance with an explicit length and part type.
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.
136
* {@link #SKIP_TOKEN} will be returned
137
* if tokens for the given token id should be skipped
138
* because of token id filter.
140
public Token<T> createToken(T id, int length, PartType partType) {
141
checkPartTypeNonNull(partType);
142
if (partType == PartType.COMPLETE)
143
return createToken(id, length);
145
if (isSkipToken(id)) {
146
operation.tokenRecognized(length, true);
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);
153
return new PropertyToken<T>(id, operation.tokenLength(), null, partType);
159
* Get flyweight token for the given arguments.
161
* <b>Note:</b> The returned token will not be flyweight under certain
162
* conditions - see return value description.
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.
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.
172
* {@link #SKIP_TOKEN} will be returned
173
* if tokens for the given token id should be skipped
174
* because of token id filter.
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
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
194
// Check whether token with given id should be created
195
if (isSkipToken(id)) {
196
operation.tokenRecognized(text.length(), true);
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());
212
* Create token with properties.
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.
222
* {@link #SKIP_TOKEN} will be returned
223
* if tokens for the given token id should be skipped
224
* because of token id filter.
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);
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);
239
return new PropertyToken<T>(id, operation.tokenLength(),
240
propertyProvider, partType);
246
* Create token with a custom text that possibly differs from the text
247
* represented by the token in the input text.
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);
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);
259
return new CustomTextToken<T>(id, operation.tokenLength(), text, partType);
264
private boolean isSkipToken(T id) {
265
Set<? extends TokenId> skipTokenIds = operation.skipTokenIds();
266
return (skipTokenIds != null) && skipTokenIds.contains(id);
269
@SuppressWarnings("unchecked") // NOI18N
270
private Token<T> skipToken() {
274
private void checkPartTypeNonNull(PartType partType) {
275
if (partType == null)
276
throw new IllegalArgumentException("partType must be non-null");