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:
042: package org.netbeans.modules.php.editor.completion;
043:
044: import java.io.IOException;
045: import java.util.HashSet;
046: import java.util.Set;
047: import javax.swing.text.BadLocationException;
048: import org.netbeans.modules.gsf.api.CompilationInfo;
049: import org.netbeans.api.lexer.Token;
050: import org.netbeans.api.lexer.TokenSequence;
051: import org.netbeans.editor.BaseDocument;
052: import org.netbeans.editor.Utilities;
053: import org.netbeans.modules.php.editor.TokenUtils;
054:
055: /**
056: * <code>CodeTemplateProvider</code> for selection templates.
057: *
058: * @author Victor G. Vasilyev
059: */
060: public class SelectionTemplates implements CodeTemplateProvider {
061:
062: /**
063: * Provider must have only parameterless constructor.
064: */
065: public SelectionTemplates() {
066: }
067:
068: /**
069: * @see org.netbeans.modules.php.editor.completion.CodeTemplateProvider#isApplicable(TemplateContext context)
070: */
071: public boolean isApplicable(TemplateContext context) {
072: OffsetPair selection = context.getSelectionOffsets();
073: if (!selection.isValid()) {
074: // no selection, i.e. either start or end < 0
075: // Note: only end != -1 is checked in the Ruby Module impl.
076: return true;
077: }
078: if (selection.length() <= 0) {
079: // selected nothing, i.e. start == end
080: return false;
081: }
082: try {
083: CompilationInfo info = context.getCompilationInfo();
084: BaseDocument doc = (BaseDocument) info.getDocument();
085: if (isApplicableSelectionEnvironment(doc, selection)) {
086: // There are no text to the left of the beginning or
087: // text to the right of the end
088: if (isApplicableSelectionContent(doc, selection)) {
089: return true;
090: }
091: }
092: return false;
093: } catch (IOException ex) {
094: return false;
095: }
096: }
097:
098: /**
099: * @see org.netbeans.modules.php.editor.completion.CodeTemplateProvider#getAbbreviationSet(TemplateContext context)
100: */
101: public Set<String> getAbbreviationSet(TemplateContext context) {
102: return selectionTemplates;
103: }
104:
105: /**
106: * Returns <code>true</code> if (a) there are no any non-whitespace symbols
107: * to the left and the right of the selection range or (b) selected range
108: * is started and ended on empty lines.
109: * @param doc
110: * @param selection
111: * @return
112: * @todo review conditions.
113: */
114: private boolean isApplicableSelectionEnvironment(BaseDocument doc,
115: OffsetPair selection) {
116: try {
117: int start = selection.getStartOffset();
118: int end = selection.getEndOffset();
119:
120: boolean isStartLineEmpty = Utilities.isRowEmpty(doc, start);
121: boolean isEndLineEmpty = Utilities.isRowEmpty(doc, end);
122: boolean isSelectionStartBeforeText = start <= Utilities
123: .getRowFirstNonWhite(doc, start);
124: boolean isSelectionEndAfterText = end > Utilities
125: .getRowLastNonWhite(doc, end);
126: return (isStartLineEmpty || isSelectionStartBeforeText)
127: && (isEndLineEmpty || isSelectionEndAfterText);
128: } catch (BadLocationException ex) {
129: return false;
130: }
131: }
132:
133: /**
134: * Returns <code>true</code> if (a) selected range contains only empty
135: * line(s) or applicable tokens.
136: * @param doc
137: * @param selection
138: * @return
139: */
140: private boolean isApplicableSelectionContent(BaseDocument doc,
141: OffsetPair selection) {
142: try {
143: int start = selection.getStartOffset();
144: if (!TokenUtils.checkPhp(doc, start)) {
145: return false;
146: }
147: int end = selection.getEndOffset();
148: if (!TokenUtils.checkPhp(doc, end)) {
149: return false;
150: }
151: String text = getSelectedText(doc, selection);
152: for (int i = 0; i < text.length(); i++) {
153: // TODO: Use PHP whitespaces instead.
154: if (!Character.isWhitespace(text.charAt(i))) {
155: if (isApplicableTokens(doc, start + i, end)) {
156: return true;
157: }
158: break;
159: }
160: }
161: return false;
162: } catch (BadLocationException ble) {
163: return false;
164: }
165:
166: }
167:
168: private boolean isApplicableTokens(BaseDocument doc, int start,
169: int end) {
170: // Make sure that we're not in a string etc
171: if (!TokenUtils.checkPhp(doc, start)) {
172: return false;
173: }
174: TokenSequence ts = TokenUtils.getEmbeddedTokenSequence(doc,
175: start);
176: ts.move(start);
177: if (!ts.moveNext()) {
178: return false;
179: }
180: while (ts.offset() < end) {
181: Token t = ts.token();
182: if (!isApplicableToken(t)) {
183: return false;
184: }
185: if (!ts.moveNext()) {
186: return false;
187: }
188: }
189: return true;
190: }
191:
192: private boolean isApplicableToken(Token t) {
193: if (t == null) {
194: return false;
195: }
196: String name = TokenUtils.getTokenType(t);
197: return !nonApplicableTokens.contains(name);
198: }
199:
200: private String getSelectedText(BaseDocument doc,
201: OffsetPair selection) throws BadLocationException {
202: return doc.getText(selection.getStartOffset(), selection
203: .length());
204: }
205:
206: private static final Set<String> nonApplicableTokens = new HashSet<String>();
207: static {
208: // TODO: review this list
209: nonApplicableTokens.add(TokenUtils.BLOCK_COMMENT); // NOI18N
210: nonApplicableTokens.add(TokenUtils.LINE_COMMENT); // NOI18N
211: }
212:
213: private static final Set<String> selectionTemplates = new HashSet<String>();
214: static {
215: selectionTemplates.add("if"); // NOI18N
216: selectionTemplates.add("ife"); // NOI18N
217: selectionTemplates.add("wh"); // NOI18N
218: selectionTemplates.add("do"); // NOI18N
219: selectionTemplates.add("for"); // NOI18N
220: selectionTemplates.add("fore"); // NOI18N
221: selectionTemplates.add("fork"); // NOI18N
222: selectionTemplates.add("sw"); // NOI18N
223: selectionTemplates.add("de"); // NOI18N
224: selectionTemplates.add("det"); // NOI18N
225: selectionTemplates.add("func"); // NOI18N
226: selectionTemplates.add("cl"); // NOI18N
227: selectionTemplates.add("con"); // NOI18N
228: selectionTemplates.add("des"); // NOI18N
229: }
230:
231: }
|