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.languages;
043:
044: import java.lang.reflect.InvocationTargetException;
045: import java.util.ArrayList;
046: import java.util.Collections;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Map;
051: import javax.swing.text.AbstractDocument;
052: import org.netbeans.api.languages.ASTItem;
053: import org.netbeans.api.languages.ASTNode;
054: import org.netbeans.api.languages.ASTToken;
055: import org.netbeans.api.languages.Context;
056: import org.netbeans.api.languages.SyntaxContext;
057: import org.netbeans.api.lexer.Token;
058: import org.netbeans.api.lexer.TokenSequence;
059: import org.netbeans.modules.languages.parser.Pattern;
060: import org.netbeans.modules.languages.parser.Pattern;
061: import org.openide.ErrorManager;
062: import org.openide.util.Lookup;
063:
064: /**
065: *
066: * @author Jan Jancura
067: */
068: public class Feature {
069:
070: public enum Type {
071: STRING, METHOD_CALL, PATTERN, NOT_SET
072: }
073:
074: public static Feature create(String featureName, Selector selector) {
075: return new Feature(featureName, selector, null, Collections
076: .<String, Evaluator> emptyMap(), Collections
077: .<String, Pattern> emptyMap());
078: }
079:
080: public static Feature createMethodCallFeature(String featureName,
081: Selector selector, String methodCall) {
082: return new Feature(featureName, selector,
083: new Method(methodCall), Collections
084: .<String, Evaluator> emptyMap(), Collections
085: .<String, Pattern> emptyMap());
086: }
087:
088: public static Feature createExpressionFeature(String featureName,
089: Selector selector, String expression) {
090: return new Feature(featureName, selector, new Expression(
091: expression),
092: Collections.<String, Evaluator> emptyMap(), Collections
093: .<String, Pattern> emptyMap());
094: }
095:
096: public static Feature createExpressionFeature(String featureName,
097: Selector selector, Pattern pattern) {
098: return new Feature(featureName, selector, pattern, Collections
099: .<String, Evaluator> emptyMap(), Collections
100: .<String, Pattern> emptyMap());
101: }
102:
103: public static Feature create(String featureName, Selector selector,
104: Map<String, String> expressions,
105: Map<String, String> methods, Map<String, Pattern> patterns) {
106: Map<String, Evaluator> evaluators = new HashMap<String, Evaluator>();
107: Iterator<String> it = expressions.keySet().iterator();
108: while (it.hasNext()) {
109: String key = it.next();
110: evaluators.put(key, new Expression(expressions.get(key)));
111: }
112: it = methods.keySet().iterator();
113: while (it.hasNext()) {
114: String key = it.next();
115: evaluators.put(key, new Method(methods.get(key)));
116: }
117: return new Feature(featureName, selector, null, evaluators,
118: patterns);
119: }
120:
121: private String featureName;
122: private Selector selector;
123: private Object value;
124: private Map<String, Evaluator> evaluators;
125: private Map<String, Pattern> patterns;
126:
127: private Feature(String featureName, Selector selector,
128: Object value, Map<String, Evaluator> evaluators,
129: Map<String, Pattern> patterns) {
130: this .featureName = featureName;
131: this .selector = selector;
132: this .value = value;
133: this .evaluators = evaluators;
134: this .patterns = patterns;
135: }
136:
137: public String getFeatureName() {
138: return featureName;
139: }
140:
141: public Selector getSelector() {
142: return selector;
143: }
144:
145: public boolean hasSingleValue() {
146: return value != null;
147: }
148:
149: public Type getType() {
150: if (value == null)
151: return Type.NOT_SET;
152: if (value instanceof Pattern)
153: return Type.PATTERN;
154: if (value instanceof Method)
155: return Type.METHOD_CALL;
156: return Type.STRING;
157: }
158:
159: public Object getValue() {
160: if (value instanceof Evaluator)
161: return ((Evaluator) value).evaluate();
162: return value;
163: }
164:
165: public Pattern getPattern() {
166: return (Pattern) value;
167: }
168:
169: public Object getValue(Context context) {
170: if (value == null)
171: return null;
172: return ((Evaluator) value).evaluate(context);
173: }
174:
175: public Object getValue(Object[] parameters) {
176: if (value == null)
177: return null;
178: return ((Method) value).evaluate(parameters);
179: }
180:
181: public boolean getBoolean(String propertyName, boolean defaultValue) {
182: Object o = getValue(propertyName);
183: if (o == null)
184: return defaultValue;
185: if (o instanceof Boolean)
186: return ((Boolean) o).booleanValue();
187: return Boolean.parseBoolean((String) o);
188: }
189:
190: public boolean getBoolean(String propertyName, Context context,
191: boolean defaultValue) {
192: Object o = getValue(propertyName, context);
193: if (o == null)
194: return defaultValue;
195: if (o instanceof Boolean)
196: return ((Boolean) o).booleanValue();
197: return Boolean.parseBoolean((String) o);
198: }
199:
200: public Object getValue(String propertyName) {
201: Evaluator e = evaluators.get(propertyName);
202: if (e != null)
203: return e.evaluate();
204: return patterns.get(propertyName);
205: }
206:
207: public Object getValue(String propertyName, Context context) {
208: Evaluator e = evaluators.get(propertyName);
209: if (e == null)
210: return null;
211: return e.evaluate(context);
212: }
213:
214: public Object getValue(String propertyName, Object[] parameters) {
215: Method e = (Method) evaluators.get(propertyName);
216: if (e == null)
217: return null;
218: return e.evaluate(parameters);
219: }
220:
221: public Pattern getPattern(String propertyName) {
222: return patterns.get(propertyName);
223: }
224:
225: public Type getType(String propertyName) {
226: if (patterns.containsKey(propertyName))
227: return Type.PATTERN;
228: Evaluator e = evaluators.get(propertyName);
229: if (e == null)
230: return Type.NOT_SET;
231: if (e instanceof Method)
232: return Type.METHOD_CALL;
233: return Type.STRING;
234: }
235:
236: public String getMethodName() {
237: return ((Method) value).getMethodName();
238: }
239:
240: public String getMethodName(String propertyName) {
241: Method m = (Method) evaluators.get(propertyName);
242: if (m == null)
243: return null;
244: return m.getMethodName();
245: }
246:
247: public String toString() {
248: StringBuilder sb = new StringBuilder();
249: sb.append("Feature ");
250: if (featureName != null)
251: sb.append(featureName).append(' ');
252: if (selector != null)
253: sb.append(selector).append(' ');
254: if (value != null)
255: sb.append(value).append(' ');
256: return sb.toString();
257: }
258:
259: // innerclasses ............................................................
260:
261: private abstract static class Evaluator {
262: public abstract Object evaluate();
263:
264: public abstract Object evaluate(Context context);
265: }
266:
267: private static class Expression extends Evaluator {
268:
269: private String[] names;
270: private String expression;
271:
272: private Expression(String expression) {
273: this .expression = expression;
274: if (expression == null)
275: return;
276: List<String> l = new ArrayList<String>();
277: int start = 0;
278: do {
279: int ss = expression.indexOf('$', start);
280: if (ss < 0) {
281: l.add(expression.substring(start, expression
282: .length()));
283: break;
284: }
285: l.add(expression.substring(start, ss));
286: ss++;
287: int se = expression.indexOf('$', ss);
288: if (se < 0)
289: se = expression.length();
290: l.add(expression.substring(ss, se));
291: start = se + 1;
292: } while (start < expression.length());
293: names = l.toArray(new String[l.size()]);
294: }
295:
296: public Object evaluate(Context context) {
297: if (context instanceof SyntaxContext) {
298: Object l = ((SyntaxContext) context).getASTPath()
299: .getLeaf();
300: if (l instanceof ASTNode)
301: return evaluate((ASTNode) l);
302: if (l instanceof ASTToken)
303: return evaluate((ASTToken) l);
304: } else {
305: AbstractDocument document = (AbstractDocument) context
306: .getDocument();
307: document.readLock();
308: ASTToken stoken = null;
309: try {
310: TokenSequence tokenSequence = Utils
311: .getTokenSequence(document, context
312: .getOffset());
313: Token token = tokenSequence.token();
314: if (token != null)
315: stoken = ASTToken.create(null, token.id()
316: .ordinal(), token.text().toString(),
317: tokenSequence.offset());
318: } finally {
319: document.readUnlock();
320: }
321: return evaluate(stoken);
322: }
323: throw new IllegalArgumentException();
324: }
325:
326: public Object evaluate() {
327: return expression;
328: }
329:
330: private Object evaluate(ASTNode node) {
331: if (names == null)
332: return null;
333: StringBuilder sb = new StringBuilder();
334: int i, k = names.length;
335: for (i = 0; i < k; i += 2) {
336: sb.append(names[i]);
337: if (i + 1 >= names.length)
338: break;
339: if (names[i + 1].equals("")) {
340: sb.append(node.getAsText());
341: continue;
342: }
343: ASTItem item = get(node, names[i + 1]);
344: if (item instanceof ASTToken)
345: sb.append(((ASTToken) item).getIdentifier());
346: else if (item instanceof ASTNode)
347: sb.append(((ASTNode) item).getAsText());
348: }
349: return sb.toString();
350: }
351:
352: private static ASTItem get(ASTNode node, String s) {
353: int i = s.indexOf('.');
354: if (i > 0) {
355: String ss = s.substring(0, i);
356: ASTNode n = node.getNode(ss);
357: if (n != null)
358: return get(n, s.substring(i + 1));
359: return null;
360: }
361: ASTNode n = node.getNode(s);
362: if (n != null)
363: return n;
364: return node.getTokenType(s);
365: }
366:
367: private String evaluate(ASTToken token) {
368: if (names == null)
369: return null;
370: StringBuilder sb = new StringBuilder();
371: int i, k = names.length;
372: for (i = 0; i < k; i += 2) {
373: sb.append(names[i]);
374: if (i + 1 >= names.length)
375: break;
376: if (names[i + 1].equals("identifier"))
377: sb.append(token.getIdentifier());
378: else if (names[i + 1].equals(""))
379: sb.append(token.getIdentifier());
380: else if (names[i + 1].equals("type"))
381: sb.append(token.getTypeName());
382: }
383: return sb.toString();
384: }
385: }
386:
387: private static class Method extends Evaluator {
388:
389: private String methodName;
390: private java.lang.reflect.Method method;
391: private boolean resolved = false;
392:
393: private Method(String methodName) {
394: this .methodName = methodName;
395: }
396:
397: public Object evaluate() {
398: return evaluate(new Object[] {});
399: }
400:
401: public Object evaluate(Context context) {
402: return evaluate(new Object[] { context });
403: }
404:
405: // public Object evaluate (ASTToken token) {
406: // return evaluate (new Object[] {token});
407: // }
408:
409: public Object evaluate(Object[] params) {
410: if (!resolved) {
411: resolved = true;
412: int i = methodName.lastIndexOf('.');
413: if (i < 1)
414: throw new IllegalArgumentException(methodName);
415: String className = methodName.substring(0, i);
416: String methodN = methodName.substring(i + 1);
417: ClassLoader cl = (ClassLoader) Lookup.getDefault()
418: .lookup(ClassLoader.class);
419: try {
420: Class cls = cl.loadClass(className);
421: java.lang.reflect.Method[] ms = cls.getMethods();
422: int j, jj = ms.length;
423: for (j = 0; j < jj; j++)
424: if (ms[j].getName().equals(methodN)
425: && ms[j].getParameterTypes().length == params.length) {
426: Class[] pts = ms[j].getParameterTypes();
427: int l, ll = params.length;
428: for (l = 0; l < ll; l++) {
429: if (params[l] != null
430: && !pts[l]
431: .isAssignableFrom(params[l]
432: .getClass()))
433: break;
434: }
435: if (l < ll)
436: continue;
437: method = ms[j];
438: break;
439: }
440: if (method == null)
441: throw new NoSuchMethodException(methodName);
442: } catch (ClassNotFoundException ex) {
443: ErrorManager.getDefault().notify(ex);
444: } catch (NoSuchMethodException ex) {
445: ErrorManager.getDefault().notify(ex);
446: }
447: }
448: if (method != null)
449: try {
450: return method.invoke(null, params);
451: } catch (IllegalAccessException ex) {
452: ErrorManager.getDefault().notify(ex);
453: } catch (InvocationTargetException ex) {
454: ErrorManager.getDefault().notify(ex);
455: } catch (IllegalArgumentException ex) {
456: ErrorManager.getDefault().notify(ex);
457: }
458: return null;
459: }
460:
461: public String getMethodName() {
462: return methodName;
463: }
464: }
465: }
|