001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.php.editor;
042:
043: import javax.swing.text.AbstractDocument;
044: import javax.swing.text.Document;
045:
046: import org.netbeans.api.languages.ASTNode;
047: import org.netbeans.api.lexer.Token;
048: import org.netbeans.api.lexer.TokenHierarchy;
049: import org.netbeans.api.lexer.TokenSequence;
050: import org.netbeans.editor.BaseDocument;
051: import org.netbeans.modules.editor.NbEditorUtilities;
052: import org.netbeans.modules.php.lexer.PhpTokenId;
053: import org.netbeans.modules.php.model.ModelAccess;
054: import org.netbeans.modules.php.model.PhpModel;
055:
056: /**
057: * @author ads
058: *
059: */
060: public class TokenUtils {
061:
062: public static String RBRACKET = "]"; // NOI18N
063:
064: public static String RPAREN = ")"; // NOI18N
065:
066: public static String LBRACKET = "["; // NOI18N
067:
068: public static String LPAREN = "("; // NOI18N
069:
070: public static String SEMICOLON = ";"; // NOI18N
071:
072: public static String LBRACE = "{"; // NOI18N
073:
074: public static String RBRACE = "}"; // NOI18N
075:
076: public static String FOR = "for"; // NOI18N
077:
078: public static String CASE = "case"; // NOI18N
079:
080: public static String DOT = "."; // NOI18N
081:
082: public static String COLON = ":"; // NOI18N
083:
084: public static String COMMA = ","; // NOI18N
085:
086: public static String ARRAY_PAIR_MAPPER = "=>"; // NOI18N
087:
088: public static String WHITESPACE = "php_whitespace"; // NOI18N
089:
090: public static String STRING = "php_string"; // NOI18N
091:
092: public static String BLOCK_COMMENT = "php_comment"; // NOI18N
093:
094: public static String LINE_COMMENT = "php_line_comment"; // NOI18N
095:
096: public static String EOD_STRING = "php_eod_string"; // NOI18N
097:
098: public static String EOD_OPERATOR = "php_eod_operator"; // NOI18N
099:
100: public enum PHPTokenName {
101: OPERATOR("php_operator"), // NOI18N
102: WHITESPACE("php_whitespace"), // NOI18N
103: SEPARATOR("php_separator"), // NOI18N
104: BLOCK_COMMENT("php_comment"), // NOI18N
105: LINE_COMMENT("php_line_comment"), // NOI18N
106: EOD_STRING("php_eod_string"), // NOI18N
107: EOD_OPERATOR("php_eod_operator"); // NOI18N
108:
109: PHPTokenName(String value) {
110: this .value = value;
111: }
112:
113: private final String value;
114:
115: public String value() {
116: return value;
117: }
118: }
119:
120: private TokenUtils() {
121: }
122:
123: public static Token getToken(TokenSequence seq, int offset) {
124: seq.move(offset);
125: if (!seq.moveNext() && !seq.movePrevious()) {
126: return null;
127: }
128: return seq.token();
129: }
130:
131: /**
132: * Returns text of an entried token located nearly the caret.
133: * @param seq a token sequence
134: * @param offset location of the caret
135: * @param upToOffset If true, provide a text only up to the caretOffset.
136: * Otherwise, compute the text of the token under the caret.
137: * @return text of the token located at\before the specified
138: * <code>offset</code> or <code>null</code> if there are no tokens in both
139: * the backward direction and the forward direction.
140: * @throws ConcurrentModificationException if this token sequence is no
141: * longer valid because of an underlying mutable input source modification.
142: */
143: public static String getEnteredTokenText(TokenSequence seq,
144: int offset, boolean upToOffset) {
145: String text = null;
146: int diff = seq.move(offset);
147: if (diff == 0 ? seq.movePrevious() : seq.moveNext()) {
148: Token token = seq.token();
149: if (token != null) {
150: text = token.text().toString();
151: if (upToOffset && diff != 0) {
152: text = text.substring(0, diff);
153: }
154: }
155: }
156: return text;
157: }
158:
159: /**
160: * Returns an entried token located nearly the caret.
161: * @param seq a token sequence
162: * @param offset location of the caret
163: * @return the token located at\before the specified
164: * <code>offset</code> or <code>null</code> if there are no tokens in both
165: * the backward direction and the forward direction.
166: * @throws ConcurrentModificationException if this token sequence is no
167: * longer valid because of an underlying mutable input source modification.
168: */
169: public static Token getEnteredToken(TokenSequence seq, int offset) {
170: Token token = null;
171: int diff = seq.move(offset);
172: if (diff == 0 ? seq.movePrevious() : seq.moveNext()) {
173: token = seq.token();
174: }
175: return token;
176: }
177:
178: /**
179: * returns offset of token which starts on or inclides specified offset.
180: */
181: public static int getTokenOffset(TokenSequence seq, int offset) {
182: seq.move(offset);
183: if (!seq.moveNext() && !seq.movePrevious()) {
184: return -1;
185: }
186: return seq.offset();
187: }
188:
189: /**
190: * returns offset of token which starts on or inclides specified offset.
191: */
192: public static int getTokenOffset(BaseDocument doc, int offset) {
193: TokenSequence sequence = getTokenSequence(doc);
194: return getTokenOffset(sequence, offset);
195: }
196:
197: public static boolean checkPhp(Document doc, int offset) {
198: Token token = TokenUtils.getToken(doc, offset);
199: return token != null && token.id() == PhpTokenId.PHP;
200: }
201:
202: public static TokenSequence getEmbeddedTokenSequence(Document doc,
203: int offset) {
204: assert doc instanceof AbstractDocument;
205: AbstractDocument document = (AbstractDocument) doc;
206: document.readLock();
207: try {
208: TokenHierarchy hierarchy = TokenHierarchy.get(doc);
209: TokenSequence seq = hierarchy.tokenSequence();
210: seq.move(offset);
211: if (!seq.moveNext() && !seq.movePrevious()) {
212: return null;
213: }
214: return seq.embedded();
215: } finally {
216: document.readUnlock();
217: }
218: }
219:
220: public static Token getToken(Document document, int offset) {
221: assert document instanceof AbstractDocument;
222: AbstractDocument doc = (AbstractDocument) document;
223: doc.readLock();
224: try {
225: TokenHierarchy hierarchy = TokenHierarchy.get(doc);
226: TokenSequence seq = hierarchy.tokenSequence();
227: return getToken(seq, offset);
228: } finally {
229: doc.readUnlock();
230: }
231: }
232:
233: public static Token getPhpToken(BaseDocument document, int offset) {
234: TokenSequence sequence = getTokenSequence(document);
235: return getToken(sequence, offset);
236: }
237:
238: public static Token getEmbeddedToken(Document document, int offset) {
239: TokenSequence sequence = getEmbeddedTokenSequence(document,
240: offset);
241: return getToken(sequence, offset);
242: }
243:
244: public static String getEnteredEmbeddedTokenText(Document document,
245: int offset, boolean upToOffset) {
246: TokenSequence sequence = getEmbeddedTokenSequence(document,
247: offset);
248: return getEnteredTokenText(sequence, offset, upToOffset);
249: }
250:
251: public static TokenSequence<?> getTokenSequence(BaseDocument doc) {
252: PhpModel model = ModelAccess.getAccess().getModel(
253: ModelAccess.getModelOrigin(NbEditorUtilities
254: .getFileObject(doc)));
255: model.writeLock();
256: try {
257: model.sync();
258: return model.getLookup().lookup(TokenSequence.class);
259: } finally {
260: model.writeUnlock();
261: }
262: }
263:
264: public static ASTNode getRoot(BaseDocument doc) {
265: PhpModel model = ModelAccess.getAccess().getModel(
266: ModelAccess.getModelOrigin(NbEditorUtilities
267: .getFileObject(doc)));
268: model.writeLock();
269: try {
270: model.sync();
271: return model.getLookup().lookup(ASTNode.class);
272: } finally {
273: model.writeUnlock();
274: }
275: }
276:
277: public static String getTokenType(Token token) {
278: return token.id().name();
279: }
280:
281: /**
282: * Returns text of the nearest <code>BLOCK_COMMENT</code> token that is located
283: * before the specified <code>offset</code> and whose text is started with
284: * "/**".
285: * @param document a target document
286: * @param offset an offest
287: * @return the text of the <code>BLOCK_COMMENT</code> token if it is possible,
288: * otherwise <code>null</code>.
289: */
290: public static String getDocComentText(Document document, int offset) {
291: TokenSequence ts = getEmbeddedTokenSequence(document, offset);
292: if (ts == null) {
293: return null;
294: }
295: ts.move(offset);
296: while (ts.movePrevious()) {
297: Token token = ts.token();
298: if (token == null) {
299: return null;
300: }
301: String tokenType = getTokenType(token);
302: if (WHITESPACE.equals(tokenType)) {
303: continue;
304: } else if (BLOCK_COMMENT.equals(tokenType)) {
305: String text = token.text().toString();
306: if (text.startsWith("/**")) {
307: String docCommentText = text;
308: return docCommentText;
309: }
310: }
311: break;
312: }
313: return null;
314: }
315:
316: }
|