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

Last change on this file since 7015 was 7015, checked in by Don-vip, 10 years ago

see #8465 - fix generics for JComboBox/JList

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