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.completion;
042:
043: import java.util.Collection;
044: import java.util.HashMap;
045: import java.util.LinkedList;
046: import java.util.List;
047: import java.util.Map;
048: import java.util.Map.Entry;
049:
050: import org.netbeans.modules.gsf.api.CompletionProposal;
051: import org.netbeans.modules.gsf.api.HtmlFormatter;
052: import org.netbeans.modules.languages.php.lang.PseudoVariables;
053: import org.netbeans.modules.php.editor.completion.VariableItem.VarTypes;
054: import org.netbeans.modules.php.model.ClassBody;
055: import org.netbeans.modules.php.model.ClassDefinition;
056: import org.netbeans.modules.php.model.ClassFunctionDefinition;
057: import org.netbeans.modules.php.model.Expression;
058: import org.netbeans.modules.php.model.GlobalStatement;
059: import org.netbeans.modules.php.model.InitializedDeclaration;
060: import org.netbeans.modules.php.model.Literal;
061: import org.netbeans.modules.php.model.Modifier;
062: import org.netbeans.modules.php.model.SourceElement;
063: import org.netbeans.modules.php.model.StaticStatement;
064: import org.netbeans.modules.php.model.Variable;
065: import org.netbeans.modules.php.model.VariableAppearance;
066: import org.netbeans.modules.php.model.VariableDeclaration;
067: import org.netbeans.modules.php.model.refs.ReferenceResolver;
068: import org.openide.util.Lookup;
069:
070: /**
071: * @author ads
072: * @author Victor G. Vasilyev
073: */
074: public class VariableProvider implements CompletionResultProvider {
075:
076: private static final String GLOBALS = "$GLOBALS"; // NOI18N
077: private static final String SERVER = "$_SERVER"; // NOI18N
078: private static final String GET = "$_GET"; // NOI18N
079: private static final String POST = "$_POST"; // NOI18N
080: private static final String COOKIE = "$_COOKIE"; // NOI18N
081: private static final String FILES = "$_FILES"; // NOI18N
082: private static final String ENV = "$_ENV"; // NOI18N
083: private static final String REQUEST = "$_REQUEST"; // NOI18N
084: private static final String SESSION = "$_SESSION"; // NOI18N
085:
086: /**
087: * @see org.netbeans.modules.php.editor.completion.CompletionResultProvider#isApplicable(org.netbeans.modules.php.editor.completion.CodeCompletionContext)
088: * @todo Support of Variable variables like this $$a
089: */
090: public boolean isApplicable(CodeCompletionContext context) {
091: if (context.getPrefix() == null) {
092: return false;
093: }
094: SourceElement e = context.getCurrentSourceElement();
095: if (e == null) {
096: return false;
097: }
098: while (e != null) {
099: if (e.getElementType().equals(Variable.class)) {
100: if (e instanceof Variable) {
101: Variable var = (Variable) e;
102: Expression expression = var.getName();
103: if (expression != null
104: && expression.getElementType().equals(
105: Literal.class)) {
106: return true;
107: }
108: }
109: }
110: e = e.getParent();
111: context.setCurrentSourceElement(e);
112: }
113: return false;
114: }
115:
116: /* (non-Javadoc)
117: * @see org.netbeans.modules.php.editor.completion.CompletionResultProvider#getProposals(org.netbeans.modules.php.editor.completion.CodeCompletionContext)
118: */
119: public List<CompletionProposal> getProposals(
120: CodeCompletionContext context) {
121: List<CompletionProposal> list = new LinkedList<CompletionProposal>();
122: addSuperglobal(list, context);
123: addUserDefined(list, context);
124: addPseudoVariable(list, context);
125: return list;
126: }
127:
128: /**
129: * Adds a <code>CompletionProposal</code> for the pseudo-variable $this
130: * if the specified <code>CodeCompletionContext</code> is located in the
131: * class definition scope.
132: *
133: * @param list the target list of the <code>CompletionProposal</code>s.
134: * @param context the code completion context.
135: */
136: public static void addPseudoVariable(List<CompletionProposal> list,
137: CodeCompletionContext context) {
138:
139: int caretOffset = context.getCaretOffset();
140: String prefix = context.getPrefix();
141: HtmlFormatter formatter = context.getFormatter();
142: String var = PseudoVariables.THIS.value();
143:
144: if (!startsWith(var, prefix)) {
145: return;
146: }
147: SourceElement e = context.getCurrentSourceElement();
148: ClassFunctionDefinition cfd = getOuterMethod(e);
149: if (cfd == null) {
150: return;
151: }
152: // See http://www.php.net/manual/en/language.oop5.static.php
153: // the pseudo variable $this is not available inside the method declared
154: // as static.
155: if (isStaticMethod(cfd)) {
156: return;
157: }
158: if (!isClassDefinitionScope(cfd)) {
159: return;
160: }
161: list.add(new VariableItem(var, caretOffset - prefix.length(),
162: VarTypes.PSEUDO, formatter, false));
163: }
164:
165: private static boolean isStaticMethod(ClassFunctionDefinition fd) {
166: int actualFlags = Modifier.toFlags(fd.getModifiers());
167: return Modifier.STATIC.isOn(actualFlags);
168: }
169:
170: private static boolean isClassDefinitionScope(
171: ClassFunctionDefinition cfd) {
172: ClassDefinition cd = getOuterClass(cfd);
173: if (cd == null) {
174: return false;
175: }
176: return true;
177: }
178:
179: private static ClassDefinition getOuterClass(
180: ClassFunctionDefinition cfd) {
181: SourceElement e = cfd;
182: ClassDefinition cd = null;
183: while (e != null) {
184: if (e.getElementType().equals(ClassBody.class)) {
185: e = e.getParent();
186: if (e instanceof ClassDefinition) {
187: return (ClassDefinition) e;
188: } else {
189: return null;
190: }
191: }
192: e = e.getParent();
193: }
194: return cd;
195: }
196:
197: private static ClassFunctionDefinition getOuterMethod(
198: SourceElement e) {
199: ClassFunctionDefinition cfd = null;
200: while (e != null) {
201: if (e.getElementType()
202: .equals(ClassFunctionDefinition.class)) {
203: if (e instanceof ClassFunctionDefinition) {
204: return (ClassFunctionDefinition) e;
205: }
206: }
207: e = e.getParent();
208: }
209: return cfd;
210: }
211:
212: public static void addUserDefined(List<CompletionProposal> list,
213: CodeCompletionContext context) {
214:
215: List<ReferenceResolver> resolvers = getResolvers();
216: SourceElement currentElement = context.getSourceElement();
217: int caretOffset = context.getCaretOffset();
218: String prefix = context.getPrefix();
219: HtmlFormatter formatter = context.getFormatter();
220:
221: Map<String, VariableAppearance> map = new HashMap<String, VariableAppearance>();
222:
223: for (ReferenceResolver resolver : resolvers) {
224: List<VariableAppearance> vars = resolver.resolve(
225: currentElement, prefix, VariableAppearance.class,
226: false);
227: for (VariableAppearance appearance : vars) {
228: SourceElement e = context.getCurrentSourceElement();
229: if (appearance.equals(e)) {
230: continue;
231: }
232: collectVars(appearance, map);
233: }
234: }
235: for (Entry<String, VariableAppearance> entry : map.entrySet()) {
236: String name = entry.getKey();
237: VariableAppearance var = entry.getValue();
238: VarTypes type = VarTypes.LOCAL;
239: if (var.getParent() != null
240: && var.getParent().getElementType().equals(
241: GlobalStatement.class)) {
242: type = VarTypes.GLOBAL;
243: } else if (var.getParent() != null
244: && var.getParent().getElementType().equals(
245: StaticStatement.class)) {
246: type = VarTypes.STATIC;
247: }
248: if (!SUPERGLOBAL_VARIABLE_NAMES.contains(name)) { // exclude
249: list.add(new VariableItem(name, caretOffset
250: - prefix.length(), type, formatter, false));
251: }
252: }
253: }
254:
255: public static void addSuperglobal(List<CompletionProposal> list,
256: CodeCompletionContext context) {
257: int caretOffset = context.getCaretOffset();
258: String prefix = context.getPrefix();
259: HtmlFormatter formatter = context.getFormatter();
260:
261: for (String var : SUPERGLOBAL_VARIABLE_NAMES) {
262: if (startsWith(var, prefix)) {
263: list.add(new VariableItem(var, caretOffset
264: - prefix.length(), VarTypes.PREDEFINED,
265: formatter, true));
266: }
267: }
268: }
269:
270: private static boolean startsWith(String variableName, String prefix) {
271: if (variableName == null || prefix == null) {
272: return false;
273: }
274: return variableName.toLowerCase().startsWith(
275: prefix.toLowerCase());
276: }
277:
278: public static void collectVars(VariableAppearance appearance,
279: Map<String, VariableAppearance> collectedVars) {
280: String name = appearance.getText();
281: VariableAppearance var = collectedVars.get(name);
282: // TODO: Only actual var decl should be added.
283: // e.g. in case $v->m() , the var $v sould not be added.
284: if (var == null) {
285: collectedVars.put(name, appearance);
286: } else {
287: if (!var.getElementType().equals(VariableDeclaration.class)
288: && !var.getElementType().equals(
289: InitializedDeclaration.class)) {
290: collectedVars.put(name, appearance);
291: }
292: }
293: }
294:
295: public static List<ReferenceResolver> getResolvers() {
296: List<ReferenceResolver> resolvers = new LinkedList<ReferenceResolver>();
297: Collection<? extends ReferenceResolver> collection = Lookup
298: .getDefault().lookupAll(ReferenceResolver.class);
299: for (ReferenceResolver resolver : collection) {
300: if (resolver.isApplicable(VariableAppearance.class)) {
301: resolvers.add(resolver);
302: }
303: }
304: return resolvers;
305: }
306:
307: /**
308: * List of the 'superglobal', or automatic global, variable names.
309: * @see the Appendix M. List of Reserved Words section of
310: * the Appendix M. List of Reserved Words of th PHP Manual
311: */
312: private static List<String> SUPERGLOBAL_VARIABLE_NAMES = new LinkedList<String>();
313:
314: static {
315: SUPERGLOBAL_VARIABLE_NAMES.add(GLOBALS);
316: SUPERGLOBAL_VARIABLE_NAMES.add(SERVER);
317: SUPERGLOBAL_VARIABLE_NAMES.add(GET);
318: SUPERGLOBAL_VARIABLE_NAMES.add(POST);
319: SUPERGLOBAL_VARIABLE_NAMES.add(COOKIE);
320: SUPERGLOBAL_VARIABLE_NAMES.add(FILES);
321: SUPERGLOBAL_VARIABLE_NAMES.add(ENV);
322: SUPERGLOBAL_VARIABLE_NAMES.add(REQUEST);
323: SUPERGLOBAL_VARIABLE_NAMES.add(SESSION);
324: }
325: }
|