source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java@ 6896

Last change on this file since 6896 was 6896, checked in by bastiK, 10 years ago

mapcss: add support for @media expressions
includes part of the style sheet under certain condition
e.g. min josm version.

There is a major limitation: the intire file is still parsed.
So if new syntax is introduced, it will still generate
a parsing error for old josm version even if the new code
is wrapped in an @media section.

File size: 27.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.mapcss;
3
4import static org.openstreetmap.josm.tools.Utils.equal;
5
6import java.awt.Color;
7import java.io.UnsupportedEncodingException;
8import java.lang.annotation.ElementType;
9import java.lang.annotation.Retention;
10import java.lang.annotation.RetentionPolicy;
11import java.lang.annotation.Target;
12import java.lang.reflect.Array;
13import java.lang.reflect.InvocationTargetException;
14import java.lang.reflect.Method;
15import java.net.URLEncoder;
16import java.util.ArrayList;
17import java.util.Arrays;
18import java.util.List;
19import java.util.regex.Matcher;
20import java.util.regex.Pattern;
21
22import org.openstreetmap.josm.Main;
23import org.openstreetmap.josm.actions.search.SearchCompiler;
24import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
25import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
26import org.openstreetmap.josm.data.osm.OsmPrimitive;
27import org.openstreetmap.josm.gui.mappaint.Cascade;
28import org.openstreetmap.josm.gui.mappaint.Environment;
29import org.openstreetmap.josm.io.XmlWriter;
30import org.openstreetmap.josm.tools.ColorHelper;
31import org.openstreetmap.josm.tools.Utils;
32
33/**
34 * Factory to generate Expressions.
35 *
36 * See {@link #createFunctionExpression}.
37 */
38public final class ExpressionFactory {
39
40 /**
41 * Marks functions which should be executed also when one or more arguments are null.
42 */
43 @Target(ElementType.METHOD)
44 @Retention(RetentionPolicy.RUNTIME)
45 static @interface NullableArguments {}
46
47 private static final List<Method> arrayFunctions;
48 private static final List<Method> parameterFunctions;
49 private static final Functions FUNCTIONS_INSTANCE = new Functions();
50
51 static {
52 arrayFunctions = new ArrayList<Method>();
53 parameterFunctions = new ArrayList<Method>();
54 for (Method m : Functions.class.getDeclaredMethods()) {
55 Class<?>[] paramTypes = m.getParameterTypes();
56 if (paramTypes.length == 1 && paramTypes[0].isArray()) {
57 arrayFunctions.add(m);
58 } else {
59 parameterFunctions.add(m);
60 }
61 }
62 try {
63 parameterFunctions.add(Math.class.getMethod("abs", float.class));
64 parameterFunctions.add(Math.class.getMethod("acos", double.class));
65 parameterFunctions.add(Math.class.getMethod("asin", double.class));
66 parameterFunctions.add(Math.class.getMethod("atan", double.class));
67 parameterFunctions.add(Math.class.getMethod("atan2", double.class, double.class));
68 parameterFunctions.add(Math.class.getMethod("ceil", double.class));
69 parameterFunctions.add(Math.class.getMethod("cos", double.class));
70 parameterFunctions.add(Math.class.getMethod("cosh", double.class));
71 parameterFunctions.add(Math.class.getMethod("exp", double.class));
72 parameterFunctions.add(Math.class.getMethod("floor", double.class));
73 parameterFunctions.add(Math.class.getMethod("log", double.class));
74 parameterFunctions.add(Math.class.getMethod("max", float.class, float.class));
75 parameterFunctions.add(Math.class.getMethod("min", float.class, float.class));
76 parameterFunctions.add(Math.class.getMethod("random"));
77 parameterFunctions.add(Math.class.getMethod("round", float.class));
78 parameterFunctions.add(Math.class.getMethod("signum", double.class));
79 parameterFunctions.add(Math.class.getMethod("sin", double.class));
80 parameterFunctions.add(Math.class.getMethod("sinh", double.class));
81 parameterFunctions.add(Math.class.getMethod("sqrt", double.class));
82 parameterFunctions.add(Math.class.getMethod("tan", double.class));
83 parameterFunctions.add(Math.class.getMethod("tanh", double.class));
84 } catch (NoSuchMethodException ex) {
85 throw new RuntimeException(ex);
86 } catch (SecurityException ex) {
87 throw new RuntimeException(ex);
88 }
89 }
90
91 private ExpressionFactory() {
92 // Hide default constructor for utils classes
93 }
94
95 @SuppressWarnings("UnusedDeclaration")
96 public static class Functions {
97
98 Environment env;
99
100 /**
101 * Identity function for compatibility with MapCSS specification.
102 * @param o any object
103 * @return {@code o} unchanged
104 */
105 public static Object eval(Object o) {
106 return o;
107 }
108
109 public static float plus(float... args) {
110 float res = 0;
111 for (float f : args) {
112 res += f;
113 }
114 return res;
115 }
116
117 public static Float minus(float... args) {
118 if (args.length == 0) {
119 return 0.0F;
120 }
121 if (args.length == 1) {
122 return -args[0];
123 }
124 float res = args[0];
125 for (int i = 1; i < args.length; ++i) {
126 res -= args[i];
127 }
128 return res;
129 }
130
131 public static float times(float... args) {
132 float res = 1;
133 for (float f : args) {
134 res *= f;
135 }
136 return res;
137 }
138
139 public static Float divided_by(float... args) {
140 if (args.length == 0) {
141 return 1.0F;
142 }
143 float res = args[0];
144 for (int i = 1; i < args.length; ++i) {
145 if (args[i] == 0.0F) {
146 return null;
147 }
148 res /= args[i];
149 }
150 return res;
151 }
152
153 /**
154 * Creates a list of values, e.g., for the {@code dashes} property.
155 * @see Arrays#asList(Object[])
156 */
157 public static List list(Object... args) {
158 return Arrays.asList(args);
159 }
160
161 /**
162 * Returns the first non-null object. The name originates from the {@code COALESCE} SQL function.
163 * @see Utils#firstNonNull(Object[])
164 */
165 @NullableArguments
166 public static Object coalesce(Object... args) {
167 return Utils.firstNonNull(args);
168 }
169
170 /**
171 * Get the {@code n}th element of the list {@code lst} (counting starts at 0).
172 * @since 5699
173 */
174 public static Object get(List<?> lst, float n) {
175 int idx = Math.round(n);
176 if (idx >= 0 && idx < lst.size()) {
177 return lst.get(idx);
178 }
179 return null;
180 }
181
182 /**
183 * Splits string {@code toSplit} at occurrences of the separator string {@code sep} and returns a list of matches.
184 * @see String#split(String)
185 * @since 5699
186 */
187 public static List<String> split(String sep, String toSplit) {
188 return Arrays.asList(toSplit.split(Pattern.quote(sep), -1));
189 }
190
191 /**
192 * Creates a color value with the specified amounts of {@code r}ed, {@code g}reen, {@code b}lue (arguments from 0.0 to 1.0)
193 * @see Color#Color(float, float, float)
194 */
195 public static Color rgb(float r, float g, float b) {
196 Color c;
197 try {
198 c = new Color(r, g, b);
199 } catch (IllegalArgumentException e) {
200 return null;
201 }
202 return c;
203 }
204
205 /**
206 * Creates a color value from an HTML notation, i.e., {@code #rrggbb}.
207 */
208 public static Color html2color(String html) {
209 return ColorHelper.html2color(html);
210 }
211
212 /**
213 * Computes the HTML notation ({@code #rrggbb}) for a color value).
214 */
215 public static String color2html(Color c) {
216 return ColorHelper.color2html(c);
217 }
218
219 /**
220 * Get the value of the red color channel in the rgb color model
221 * @return the red color channel in the range [0;1]
222 * @see java.awt.Color#getRed()
223 */
224 public static float red(Color c) {
225 return Utils.color_int2float(c.getRed());
226 }
227
228 /**
229 * Get the value of the green color channel in the rgb color model
230 * @return the green color channel in the range [0;1]
231 * @see java.awt.Color#getGreen()
232 */
233 public static float green(Color c) {
234 return Utils.color_int2float(c.getGreen());
235 }
236
237 /**
238 * Get the value of the blue color channel in the rgb color model
239 * @return the blue color channel in the range [0;1]
240 * @see java.awt.Color#getBlue()
241 */
242 public static float blue(Color c) {
243 return Utils.color_int2float(c.getBlue());
244 }
245
246 /**
247 * Get the value of the alpha channel in the rgba color model
248 * @return the alpha channel in the range [0;1]
249 * @see java.awt.Color#getAlpha()
250 */
251 public static float alpha(Color c) {
252 return Utils.color_int2float(c.getAlpha());
253 }
254
255 /**
256 * Assembles the strings to one.
257 * @see Utils#join
258 */
259 @NullableArguments
260 public static String concat(Object... args) {
261 return Utils.join("", Arrays.asList(args));
262 }
263
264 /**
265 * Assembles the strings to one, where the first entry is used as separator.
266 * @see Utils#join
267 */
268 @NullableArguments
269 public static String join(String... args) {
270 return Utils.join(args[0], Arrays.asList(args).subList(1, args.length));
271 }
272
273 /**
274 * Returns the value of the property {@code key}, e.g., {@code prop("width")}.
275 */
276 public Object prop(String key) {
277 return prop(key, null);
278 }
279
280 /**
281 * Returns the value of the property {@code key} from layer {@code layer}.
282 */
283 public Object prop(String key, String layer) {
284 return env.getCascade(layer).get(key);
285 }
286
287 /**
288 * Determines whether property {@code key} is set.
289 */
290 public Boolean is_prop_set(String key) {
291 return is_prop_set(key, null);
292 }
293
294 /**
295 * Determines whether property {@code key} is set on layer {@code layer}.
296 */
297 public Boolean is_prop_set(String key, String layer) {
298 return env.getCascade(layer).containsKey(key);
299 }
300
301 /**
302 * Gets the value of the key {@code key} from the object in question.
303 */
304 public String tag(String key) {
305 return env.osm == null ? null : env.osm.get(key);
306 }
307
308 /**
309 * Gets the first non-null value of the key {@code key} from the object's parent(s).
310 */
311 public String parent_tag(String key) {
312 if (env.parent == null) {
313 if (env.osm != null) {
314 // we don't have a matched parent, so just search all referrers
315 for (OsmPrimitive parent : env.osm.getReferrers()) {
316 String value = parent.get(key);
317 if (value != null) {
318 return value;
319 }
320 }
321 }
322 return null;
323 }
324 return env.parent.get(key);
325 }
326
327 /**
328 * Determines whether the object has a tag with the given key.
329 */
330 public boolean has_tag_key(String key) {
331 return env.osm.hasKey(key);
332 }
333
334 /**
335 * Returns the index of node in parent way or member in parent relation.
336 */
337 public Float index() {
338 if (env.index == null) {
339 return null;
340 }
341 return new Float(env.index + 1);
342 }
343
344 public String role() {
345 return env.getRole();
346 }
347
348 public static boolean not(boolean b) {
349 return !b;
350 }
351
352 public static boolean greater_equal(float a, float b) {
353 return a >= b;
354 }
355
356 public static boolean less_equal(float a, float b) {
357 return a <= b;
358 }
359
360 public static boolean greater(float a, float b) {
361 return a > b;
362 }
363
364 public static boolean less(float a, float b) {
365 return a < b;
366 }
367
368 /**
369 * Determines if the objects {@code a} and {@code b} are equal.
370 * @see Object#equals(Object)
371 */
372 public static boolean equal(Object a, Object b) {
373 // make sure the casts are done in a meaningful way, so
374 // the 2 objects really can be considered equal
375 for (Class<?> klass : new Class[]{Float.class, Boolean.class, Color.class, float[].class, String.class}) {
376 Object a2 = Cascade.convertTo(a, klass);
377 Object b2 = Cascade.convertTo(b, klass);
378 if (a2 != null && b2 != null && a2.equals(b2)) {
379 return true;
380 }
381 }
382 return false;
383 }
384
385 /**
386 * Determines whether the JOSM search with {@code searchStr} applies to the object.
387 */
388 public Boolean JOSM_search(String searchStr) {
389 Match m;
390 try {
391 m = SearchCompiler.compile(searchStr, false, false);
392 } catch (ParseError ex) {
393 return null;
394 }
395 return m.match(env.osm);
396 }
397
398 /**
399 * Obtains the JOSM'key {@link org.openstreetmap.josm.data.Preferences} string for key {@code key},
400 * and defaults to {@code def} if that is null.
401 * @see org.openstreetmap.josm.data.Preferences#get(String, String)
402 */
403 public static String JOSM_pref(String key, String def) {
404 String res = Main.pref.get(key, null);
405 return res != null ? res : def;
406 }
407
408 /**
409 * Obtains the JOSM'key {@link org.openstreetmap.josm.data.Preferences} color for key {@code key},
410 * and defaults to {@code def} if that is null.
411 * @see org.openstreetmap.josm.data.Preferences#getColor(String, java.awt.Color)
412 */
413 public static Color JOSM_pref_color(String key, Color def) {
414 Color res = Main.pref.getColor(key, null);
415 return res != null ? res : def;
416 }
417
418 /**
419 * Tests if string {@code target} matches pattern {@code pattern}
420 * @see Pattern#matches(String, CharSequence)
421 * @since 5699
422 */
423 public static boolean regexp_test(String pattern, String target) {
424 return Pattern.matches(pattern, target);
425 }
426
427 /**
428 * Tests if string {@code target} matches pattern {@code pattern}
429 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all")
430 * @since 5699
431 */
432 public static boolean regexp_test(String pattern, String target, String flags) {
433 int f = 0;
434 if (flags.contains("i")) {
435 f |= Pattern.CASE_INSENSITIVE;
436 }
437 if (flags.contains("s")) {
438 f |= Pattern.DOTALL;
439 }
440 if (flags.contains("m")) {
441 f |= Pattern.MULTILINE;
442 }
443 return Pattern.compile(pattern, f).matcher(target).matches();
444 }
445
446 /**
447 * Tries to match string against pattern regexp and returns a list of capture groups in case of success.
448 * The first element (index 0) is the complete match (i.e. string).
449 * Further elements correspond to the bracketed parts of the regular expression.
450 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all")
451 * @since 5701
452 */
453 public static List<String> regexp_match(String pattern, String target, String flags) {
454 int f = 0;
455 if (flags.contains("i")) {
456 f |= Pattern.CASE_INSENSITIVE;
457 }
458 if (flags.contains("s")) {
459 f |= Pattern.DOTALL;
460 }
461 if (flags.contains("m")) {
462 f |= Pattern.MULTILINE;
463 }
464 Matcher m = Pattern.compile(pattern, f).matcher(target);
465 return Utils.getMatches(m);
466 }
467
468 /**
469 * Tries to match string against pattern regexp and returns a list of capture groups in case of success.
470 * The first element (index 0) is the complete match (i.e. string).
471 * Further elements correspond to the bracketed parts of the regular expression.
472 * @since 5701
473 */
474 public static List<String> regexp_match(String pattern, String target) {
475 Matcher m = Pattern.compile(pattern).matcher(target);
476 return Utils.getMatches(m);
477 }
478
479 /**
480 * Returns the OSM id of the current object.
481 * @see OsmPrimitive#getUniqueId()
482 */
483 public long osm_id() {
484 return env.osm.getUniqueId();
485 }
486
487 /**
488 * Translates some text for the current locale. The first argument is the text to translate,
489 * and the subsequent arguments are parameters for the string indicated by {@code {0}}, {@code {1}}, …
490 */
491 @NullableArguments
492 public static String tr(String... args) {
493 final String text = args[0];
494 System.arraycopy(args, 1, args, 0, args.length - 1);
495 return org.openstreetmap.josm.tools.I18n.tr(text, (Object[])args);
496 }
497
498 /**
499 * Returns the substring of {@code s} starting at index {@code begin} (inclusive, 0-indexed).
500 * @param s The base string
501 * @param begin The start index
502 * @return the substring
503 * @see String#substring(int)
504 */
505 public static String substring(String s, /* due to missing Cascade.convertTo for int*/ float begin) {
506 return s == null ? null : s.substring((int) begin);
507 }
508
509 /**
510 * Returns the substring of {@code s} starting at index {@code begin} (inclusive)
511 * and ending at index {@code end}, (exclusive, 0-indexed).
512 * @param s The base string
513 * @param begin The start index
514 * @param end The end index
515 * @return the substring
516 * @see String#substring(int, int)
517 */
518 public static String substring(String s, float begin, float end) {
519 return s == null ? null : s.substring((int) begin, (int) end);
520 }
521
522 /**
523 * Replaces in {@code s} every {@code} target} substring by {@code replacement}.
524 * * @see String#replace(CharSequence, CharSequence)
525 */
526 public static String replace(String s, String target, String replacement) {
527 return s == null ? null : s.replace(target, replacement);
528 }
529
530 /**
531 * Percent-encode a string. (See https://en.wikipedia.org/wiki/Percent-encoding)
532 * This is especially useful for data urls, e.g.
533 * <code>icon-image: concat("data:image/svg+xml,", URL_encode("&lt;svg&gt;...&lt;/svg&gt;"));</code>
534 * @param s arbitrary string
535 * @return the encoded string
536 */
537 public static String URL_encode(String s) {
538 try {
539 return s == null ? null : URLEncoder.encode(s, "UTF-8");
540 } catch (UnsupportedEncodingException ex) {
541 throw new RuntimeException(ex);
542 }
543 }
544
545 /**
546 * XML-encode a string.
547 *
548 * Escapes special characters in xml. Alternative to using &lt;![CDATA[ ... ]]&gt; blocks.
549 * @param s arbitrary string
550 * @return the encoded string
551 */
552 public static String XML_encode(String s) {
553 return s == null ? null : XmlWriter.encode(s);
554 }
555 }
556
557 /**
558 * Main method to create an function-like expression.
559 *
560 * @param name the name of the function or operator
561 * @param args the list of arguments (as expressions)
562 * @return the generated Expression. If no suitable function can be found,
563 * returns {@link NullExpression#INSTANCE}.
564 */
565 public static Expression createFunctionExpression(String name, List<Expression> args) {
566 if (equal(name, "cond") && args.size() == 3)
567 return new CondOperator(args.get(0), args.get(1), args.get(2));
568 else if (equal(name, "and"))
569 return new AndOperator(args);
570 else if (equal(name, "or"))
571 return new OrOperator(args);
572 else if (equal(name, "length") && args.size() == 1)
573 return new LengthFunction(args.get(0));
574
575 for (Method m : arrayFunctions) {
576 if (m.getName().equals(name))
577 return new ArrayFunction(m, args);
578 }
579 for (Method m : parameterFunctions) {
580 if (m.getName().equals(name) && args.size() == m.getParameterTypes().length)
581 return new ParameterFunction(m, args);
582 }
583 return NullExpression.INSTANCE;
584 }
585
586 /**
587 * Expression that always evaluates to null.
588 */
589 public static class NullExpression implements Expression {
590
591 /**
592 * The unique instance.
593 */
594 public static final NullExpression INSTANCE = new NullExpression();
595
596 @Override
597 public Object evaluate(Environment env) {
598 return null;
599 }
600 }
601
602 /**
603 * Conditional operator.
604 */
605 public static class CondOperator implements Expression {
606
607 private Expression condition, firstOption, secondOption;
608
609 public CondOperator(Expression condition, Expression firstOption, Expression secondOption) {
610 this.condition = condition;
611 this.firstOption = firstOption;
612 this.secondOption = secondOption;
613 }
614
615 @Override
616 public Object evaluate(Environment env) {
617 Boolean b = Cascade.convertTo(condition.evaluate(env), boolean.class);
618 if (b != null && b)
619 return firstOption.evaluate(env);
620 else
621 return secondOption.evaluate(env);
622 }
623 }
624
625 public static class AndOperator implements Expression {
626
627 private List<Expression> args;
628
629 public AndOperator(List<Expression> args) {
630 this.args = args;
631 }
632
633 @Override
634 public Object evaluate(Environment env) {
635 for (Expression arg : args) {
636 Boolean b = Cascade.convertTo(arg.evaluate(env), boolean.class);
637 if (b == null || !b) {
638 return false;
639 }
640 }
641 return true;
642 }
643 }
644
645 public static class OrOperator implements Expression {
646
647 private List<Expression> args;
648
649 public OrOperator(List<Expression> args) {
650 this.args = args;
651 }
652
653 @Override
654 public Object evaluate(Environment env) {
655 for (Expression arg : args) {
656 Boolean b = Cascade.convertTo(arg.evaluate(env), boolean.class);
657 if (b != null && b) {
658 return true;
659 }
660 }
661 return false;
662 }
663 }
664
665 /**
666 * Function to calculate the length of a string or list in a MapCSS eval
667 * expression.
668 *
669 * Separate implementation to support overloading for different
670 * argument types.
671 */
672 public static class LengthFunction implements Expression {
673
674 private Expression arg;
675
676 public LengthFunction(Expression args) {
677 this.arg = args;
678 }
679
680 @Override
681 public Object evaluate(Environment env) {
682 List<?> l = Cascade.convertTo(arg.evaluate(env), List.class);
683 if (l != null)
684 return l.size();
685 String s = Cascade.convertTo(arg.evaluate(env), String.class);
686 if (s != null)
687 return s.length();
688 return null;
689 }
690 }
691
692 /**
693 * Function that takes a certain number of argument with specific type.
694 *
695 * Implementation is based on a Method object.
696 * If any of the arguments evaluate to null, the result will also be null.
697 */
698 public static class ParameterFunction implements Expression {
699
700 private final Method m;
701 private final List<Expression> args;
702 private final Class<?>[] expectedParameterTypes;
703
704 public ParameterFunction(Method m, List<Expression> args) {
705 this.m = m;
706 this.args = args;
707 expectedParameterTypes = m.getParameterTypes();
708 }
709
710 @Override
711 public Object evaluate(Environment env) {
712 FUNCTIONS_INSTANCE.env = env;
713 Object[] convertedArgs = new Object[expectedParameterTypes.length];
714 for (int i = 0; i < args.size(); ++i) {
715 convertedArgs[i] = Cascade.convertTo(args.get(i).evaluate(env), expectedParameterTypes[i]);
716 if (convertedArgs[i] == null && m.getAnnotation(NullableArguments.class) == null) {
717 return null;
718 }
719 }
720 Object result = null;
721 try {
722 result = m.invoke(FUNCTIONS_INSTANCE, convertedArgs);
723 } catch (IllegalAccessException ex) {
724 throw new RuntimeException(ex);
725 } catch (IllegalArgumentException ex) {
726 throw new RuntimeException(ex);
727 } catch (InvocationTargetException ex) {
728 Main.error(ex);
729 return null;
730 }
731 return result;
732 }
733
734 @Override
735 public String toString() {
736 StringBuilder b = new StringBuilder("ParameterFunction~");
737 b.append(m.getName()).append("(");
738 for (int i = 0; i < args.size(); ++i) {
739 if (i > 0) b.append(",");
740 b.append(expectedParameterTypes[i]);
741 b.append(" ").append(args.get(i));
742 }
743 b.append(')');
744 return b.toString();
745 }
746
747 }
748
749 /**
750 * Function that takes an arbitrary number of arguments.
751 *
752 * Currently, all array functions are static, so there is no need to
753 * provide the environment, like it is done in {@link ParameterFunction}.
754 * If any of the arguments evaluate to null, the result will also be null.
755 */
756 public static class ArrayFunction implements Expression {
757
758 private final Method m;
759 private final List<Expression> args;
760 private final Class<?> arrayComponentType;
761 private final Object[] convertedArgs;
762
763 public ArrayFunction(Method m, List<Expression> args) {
764 this.m = m;
765 this.args = args;
766 Class<?>[] expectedParameterTypes = m.getParameterTypes();
767 convertedArgs = new Object[expectedParameterTypes.length];
768 arrayComponentType = expectedParameterTypes[0].getComponentType();
769 }
770
771 @Override
772 public Object evaluate(Environment env) {
773 Object arrayArg = Array.newInstance(arrayComponentType, args.size());
774 for (int i = 0; i < args.size(); ++i) {
775 Object o = Cascade.convertTo(args.get(i).evaluate(env), arrayComponentType);
776 if (o == null && m.getAnnotation(NullableArguments.class) == null) {
777 return null;
778 }
779 Array.set(arrayArg, i, o);
780 }
781 convertedArgs[0] = arrayArg;
782
783 Object result = null;
784 try {
785 result = m.invoke(null, convertedArgs);
786 } catch (IllegalAccessException ex) {
787 throw new RuntimeException(ex);
788 } catch (IllegalArgumentException ex) {
789 throw new RuntimeException(ex);
790 } catch (InvocationTargetException ex) {
791 Main.error(ex);
792 return null;
793 }
794 return result;
795 }
796 @Override
797 public String toString() {
798 StringBuilder b = new StringBuilder("ArrayFunction~");
799 b.append(m.getName()).append("(");
800 for (int i = 0; i < args.size(); ++i) {
801 if (i > 0) b.append(",");
802 b.append(arrayComponentType);
803 b.append(" ").append(args.get(i));
804 }
805 b.append(')');
806 return b.toString();
807 }
808
809 }
810
811}
Note: See TracBrowser for help on using the repository browser.