diff --git a/docs/reference/index.asciidoc b/docs/reference/index.asciidoc index 7a2e2628102..f4629c51b09 100644 --- a/docs/reference/index.asciidoc +++ b/docs/reference/index.asciidoc @@ -20,6 +20,27 @@ release-state can be: released | prerelease | unreleased :issue: https://github.com/elastic/elasticsearch/issues/ :pull: https://github.com/elastic/elasticsearch/pull/ + +/////// +Javadoc roots used to generate links from Painless's API reference +/////// +:java8-javadoc: https://docs.oracle.com/javase/8/docs/api +:java9-javadoc: http://download.java.net/java/jigsaw/docs/api +:joda-time-javadoc: http://www.joda.org/joda-time/apidocs +:lucene-core-javadoc: http://lucene.apache.org/core/6_4_0/core + +ifeval::["{release-state}"=="unreleased"] +:elasticsearch-javadoc: https://snapshots.elastic.co/javadoc/org/elasticsearch/elasticsearch/{version}-SNAPSHOT +:painless-javadoc: https://snapshots.elastic.co/javadoc/org/elasticsearch/painless/lang-painless/{version}-SNAPSHOT +endif::[] + +ifeval::["{release-state}"!="unreleased"] +:elasticsearch-javadoc: https://artifacts.elastic.co/javadoc/org/elasticsearch/elasticsearch/{version} +:painless-javadoc: https://artifacts.elastic.co/javadoc/org/elasticsearch/painless/lang-painless/{version} +endif::[] + + + include::getting-started.asciidoc[] include::setup.asciidoc[] @@ -62,4 +83,6 @@ include::glossary.asciidoc[] include::release-notes.asciidoc[] ////// +include::painless-api-reference.asciidoc[] + include::redirects.asciidoc[] diff --git a/docs/reference/modules/scripting/painless-debugging.asciidoc b/docs/reference/modules/scripting/painless-debugging.asciidoc index eafe291d259..5bd7fe73541 100644 --- a/docs/reference/modules/scripting/painless-debugging.asciidoc +++ b/docs/reference/modules/scripting/painless-debugging.asciidoc @@ -38,15 +38,16 @@ POST /hockey/player/1/_explain // we have to override it to get a normal shaped response Which shows that the class of `doc.first` is -`org.elasticsearch.index.fielddata.ScriptDocValues$Longs` by responding with: +`org.elasticsearch.index.fielddata.ScriptDocValues.Longs` by responding with: [source,js] --------------------------------------------------------- { "error": { "type": "script_exception", - "class": "org.elasticsearch.index.fielddata.ScriptDocValues$Longs", "to_string": "[1, 9, 27]", + "painless_class": "org.elasticsearch.index.fielddata.ScriptDocValues.Longs", + "java_class": "org.elasticsearch.index.fielddata.ScriptDocValues$Longs", ... }, "status": 500 @@ -54,7 +55,7 @@ Which shows that the class of `doc.first` is --------------------------------------------------------- // TESTRESPONSE[s/\.\.\./"script_stack": $body.error.script_stack, "script": $body.error.script, "lang": $body.error.lang, "caused_by": $body.error.caused_by, "root_cause": $body.error.root_cause, "reason": $body.error.reason/] -You can use the same trick to see that `_source` is a `java.util.LinkedHashMap` +You can use the same trick to see that `_source` is a `LinkedHashMap` in the `_update` API: [source,js] @@ -78,8 +79,9 @@ The response looks like: "reason": "failed to execute script", "caused_by": { "type": "script_exception", - "class": "java.util.LinkedHashMap", "to_string": "{gp=[26, 82, 1], last=gaudreau, assists=[17, 46, 0], first=johnny, goals=[9, 27, 1]}", + "painless_class": "LinkedHashMap", + "java_class": "java.util.LinkedHashMap", ... } }, @@ -90,19 +92,5 @@ The response looks like: // TESTRESPONSE[s/\.\.\./"script_stack": $body.error.caused_by.script_stack, "script": $body.error.caused_by.script, "lang": $body.error.caused_by.lang, "caused_by": $body.error.caused_by.caused_by, "reason": $body.error.caused_by.reason/] // TESTRESPONSE[s/"to_string": ".+"/"to_string": $body.error.caused_by.to_string/] -// TODO we should build some javadoc like mashup so people don't have to jump through these hoops. - -Once you have the class of an object you can go -https://github.com/elastic/elasticsearch/tree/{branch}/modules/lang-painless/src/main/resources/org/elasticsearch/painless[here] -and check the available methods. Painless uses a strict whitelist to prevent -scripts that don't work well with Elasticsearch and all whitelisted methods -are listed in a file named after the package of the object (everything before -the last `.`). So `java.util.Map` is listed in a file named `java.util.txt` -starting on the line that looks like `class Map -> java.util.Map {`. - -With the list of whitelisted methods in hand you can turn to either -https://docs.oracle.com/javase/8/docs/api/[Javadoc], -https://github.com/elastic/elasticsearch/tree/{branch}[Elasticsearch's source tree] -or, for whitelisted methods ending in `*`, the -https://github.com/elastic/elasticsearch/blob/{branch}/modules/lang-painless/src/main/java/org/elasticsearch/painless/Augmentation.java[Augmentation] -class. +Once you have a class you can go to <> to see a list of +available methods. diff --git a/docs/reference/modules/scripting/painless.asciidoc b/docs/reference/modules/scripting/painless.asciidoc index 7995e45e7b9..a74af83eca1 100644 --- a/docs/reference/modules/scripting/painless.asciidoc +++ b/docs/reference/modules/scripting/painless.asciidoc @@ -10,8 +10,9 @@ default. The Painless syntax is similar to http://groovy-lang.org/index.html[Groovy]. -You can use Painless anywhere a script can be used in Elasticsearch--simply set the `lang` parameter -to `painless`. +You can use Painless anywhere a script can be used in Elasticsearch. It is the +default if you don't set the `lang` parameter but if you want to be explicit you +can set the `lang` parameter to `painless`. [[painless-features]] [float] @@ -19,7 +20,8 @@ to `painless`. * Fast performance: https://benchmarks.elastic.co/index.html#search_qps_scripts[several times faster] than the alternatives. -* Safety: Fine-grained <> with method call/field granularity. +* Safety: Fine-grained whitelist with method call/field granularity. See +<> for a complete list of available classes and methods. * Optional typing: Variables and parameters can use explicit types or the dynamic `def` type. @@ -320,28 +322,3 @@ Note: all of the `_update_by_query` examples above could really do with a <> it wouldn't be as efficient as using any other query because script queries aren't able to use the inverted index to limit the documents that they have to check. - -[float] -[[painless-api]] -== Painless API - -The following Java packages are available for use in the Painless language: - -* https://docs.oracle.com/javase/8/docs/api/java/lang/package-summary.html[java.lang] -* https://docs.oracle.com/javase/8/docs/api/java/math/package-summary.html[java.math] -* https://docs.oracle.com/javase/8/docs/api/java/text/package-summary.html[java.text] -* https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html[java.time] -* https://docs.oracle.com/javase/8/docs/api/java/time/chrono/package-summary.html[java.time.chrono] -* https://docs.oracle.com/javase/8/docs/api/java/time/format/package-summary.html[java.time.format] -* https://docs.oracle.com/javase/8/docs/api/java/time/temporal/package-summary.html[java.time.temporal] -* https://docs.oracle.com/javase/8/docs/api/java/time/zone/package-summary.html[java.time.zone] -* https://docs.oracle.com/javase/8/docs/api/java/util/package-summary.html[java.util] -* https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html[java.util.function] -* https://docs.oracle.com/javase/8/docs/api/java/util/regex/package-summary.html[java.util.regex] -* https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html[java.util.stream] - -Note that unsafe classes and methods are not included, there is no support for: - -* Manipulation of processes and threads -* Input/Output -* Reflection diff --git a/docs/reference/painless-api-reference.asciidoc b/docs/reference/painless-api-reference.asciidoc new file mode 100644 index 00000000000..f729c52a1bc --- /dev/null +++ b/docs/reference/painless-api-reference.asciidoc @@ -0,0 +1,17 @@ +["appendix",id="painless-api-reference"] += Painless API Reference + +<> has a strict whitelist for methods and +classes to make sure that all painless scripts are secure and fast. Most of +these methods are exposed directly from the JRE while others are part of +Elasticsearch or Painless itself. Below is a list of all available methods +grouped under the classes on which you can call them. Clicking on the method +name takes you to the documentation for the method. + +NOTE: Methods defined in the JRE also have a `(java 9)` link which can be used +to see the method's documentation in Java 9 while clicking on the method's name +goes to the Java 8 documentation. Usually these aren't different but it is +worth going to the version that matches the version of Java you are using to +run Elasticsearch just in case. + +include::painless-api-reference/index.asciidoc[] diff --git a/docs/reference/painless-api-reference/List.asciidoc b/docs/reference/painless-api-reference/List.asciidoc new file mode 100644 index 00000000000..abe438451b8 --- /dev/null +++ b/docs/reference/painless-api-reference/List.asciidoc @@ -0,0 +1,22 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-List]]++List++:: +* ++[[painless-api-reference-List-add-2]]void link:{java8-javadoc}/java/util/List.html#add%2Dint%2Djava.lang.Object%2D[add](int, def)++ (link:{java9-javadoc}/java/util/List.html#add%2Dint%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-addAll-2]]boolean link:{java8-javadoc}/java/util/List.html#addAll%2Dint%2Djava.util.Collection%2D[addAll](int, <>)++ (link:{java9-javadoc}/java/util/List.html#addAll%2Dint%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-List-equals-1]]boolean link:{java8-javadoc}/java/util/List.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/List.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-get-1]]def link:{java8-javadoc}/java/util/List.html#get%2Dint%2D[get](int)++ (link:{java9-javadoc}/java/util/List.html#get%2Dint%2D[java 9]) +* ++[[painless-api-reference-List-getLength-0]]int link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#getLength%2Djava.util.List%2D[getLength]()++ +* ++[[painless-api-reference-List-hashCode-0]]int link:{java8-javadoc}/java/util/List.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/util/List.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-List-indexOf-1]]int link:{java8-javadoc}/java/util/List.html#indexOf%2Djava.lang.Object%2D[indexOf](def)++ (link:{java9-javadoc}/java/util/List.html#indexOf%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-lastIndexOf-1]]int link:{java8-javadoc}/java/util/List.html#lastIndexOf%2Djava.lang.Object%2D[lastIndexOf](def)++ (link:{java9-javadoc}/java/util/List.html#lastIndexOf%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-listIterator-0]]<> link:{java8-javadoc}/java/util/List.html#listIterator%2D%2D[listIterator]()++ (link:{java9-javadoc}/java/util/List.html#listIterator%2D%2D[java 9]) +* ++[[painless-api-reference-List-listIterator-1]]<> link:{java8-javadoc}/java/util/List.html#listIterator%2Dint%2D[listIterator](int)++ (link:{java9-javadoc}/java/util/List.html#listIterator%2Dint%2D[java 9]) +* ++[[painless-api-reference-List-remove-1]]def link:{java8-javadoc}/java/util/List.html#remove%2Dint%2D[remove](int)++ (link:{java9-javadoc}/java/util/List.html#remove%2Dint%2D[java 9]) +* ++[[painless-api-reference-List-replaceAll-1]]void link:{java8-javadoc}/java/util/List.html#replaceAll%2Djava.util.function.UnaryOperator%2D[replaceAll](<>)++ (link:{java9-javadoc}/java/util/List.html#replaceAll%2Djava.util.function.UnaryOperator%2D[java 9]) +* ++[[painless-api-reference-List-set-2]]def link:{java8-javadoc}/java/util/List.html#set%2Dint%2Djava.lang.Object%2D[set](int, def)++ (link:{java9-javadoc}/java/util/List.html#set%2Dint%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-sort-1]]void link:{java8-javadoc}/java/util/List.html#sort%2Djava.util.Comparator%2D[sort](<>)++ (link:{java9-javadoc}/java/util/List.html#sort%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-List-subList-2]]<> link:{java8-javadoc}/java/util/List.html#subList%2Dint%2Dint%2D[subList](int, int)++ (link:{java9-javadoc}/java/util/List.html#subList%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Math.asciidoc b/docs/reference/painless-api-reference/Math.asciidoc new file mode 100644 index 00000000000..4b8322c15fc --- /dev/null +++ b/docs/reference/painless-api-reference/Math.asciidoc @@ -0,0 +1,46 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Math]]++Math++:: +** [[painless-api-reference-Math-E]]static double link:{java8-javadoc}/java/lang/Math.html#E[E] (link:{java9-javadoc}/java/lang/Math.html#E[java 9]) +** [[painless-api-reference-Math-PI]]static double link:{java8-javadoc}/java/lang/Math.html#PI[PI] (link:{java9-javadoc}/java/lang/Math.html#PI[java 9]) +* ++[[painless-api-reference-Math-IEEEremainder-2]]static double link:{java8-javadoc}/java/lang/Math.html#IEEEremainder%2Ddouble%2Ddouble%2D[IEEEremainder](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#IEEEremainder%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-abs-1]]static double link:{java8-javadoc}/java/lang/Math.html#abs%2Ddouble%2D[abs](double)++ (link:{java9-javadoc}/java/lang/Math.html#abs%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-acos-1]]static double link:{java8-javadoc}/java/lang/Math.html#acos%2Ddouble%2D[acos](double)++ (link:{java9-javadoc}/java/lang/Math.html#acos%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-asin-1]]static double link:{java8-javadoc}/java/lang/Math.html#asin%2Ddouble%2D[asin](double)++ (link:{java9-javadoc}/java/lang/Math.html#asin%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-atan-1]]static double link:{java8-javadoc}/java/lang/Math.html#atan%2Ddouble%2D[atan](double)++ (link:{java9-javadoc}/java/lang/Math.html#atan%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-atan2-2]]static double link:{java8-javadoc}/java/lang/Math.html#atan2%2Ddouble%2Ddouble%2D[atan2](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#atan2%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-cbrt-1]]static double link:{java8-javadoc}/java/lang/Math.html#cbrt%2Ddouble%2D[cbrt](double)++ (link:{java9-javadoc}/java/lang/Math.html#cbrt%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-ceil-1]]static double link:{java8-javadoc}/java/lang/Math.html#ceil%2Ddouble%2D[ceil](double)++ (link:{java9-javadoc}/java/lang/Math.html#ceil%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-copySign-2]]static double link:{java8-javadoc}/java/lang/Math.html#copySign%2Ddouble%2Ddouble%2D[copySign](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#copySign%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-cos-1]]static double link:{java8-javadoc}/java/lang/Math.html#cos%2Ddouble%2D[cos](double)++ (link:{java9-javadoc}/java/lang/Math.html#cos%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-cosh-1]]static double link:{java8-javadoc}/java/lang/Math.html#cosh%2Ddouble%2D[cosh](double)++ (link:{java9-javadoc}/java/lang/Math.html#cosh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-exp-1]]static double link:{java8-javadoc}/java/lang/Math.html#exp%2Ddouble%2D[exp](double)++ (link:{java9-javadoc}/java/lang/Math.html#exp%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-expm1-1]]static double link:{java8-javadoc}/java/lang/Math.html#expm1%2Ddouble%2D[expm1](double)++ (link:{java9-javadoc}/java/lang/Math.html#expm1%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-floor-1]]static double link:{java8-javadoc}/java/lang/Math.html#floor%2Ddouble%2D[floor](double)++ (link:{java9-javadoc}/java/lang/Math.html#floor%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-hypot-2]]static double link:{java8-javadoc}/java/lang/Math.html#hypot%2Ddouble%2Ddouble%2D[hypot](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#hypot%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-log-1]]static double link:{java8-javadoc}/java/lang/Math.html#log%2Ddouble%2D[log](double)++ (link:{java9-javadoc}/java/lang/Math.html#log%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-log10-1]]static double link:{java8-javadoc}/java/lang/Math.html#log10%2Ddouble%2D[log10](double)++ (link:{java9-javadoc}/java/lang/Math.html#log10%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-log1p-1]]static double link:{java8-javadoc}/java/lang/Math.html#log1p%2Ddouble%2D[log1p](double)++ (link:{java9-javadoc}/java/lang/Math.html#log1p%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-max-2]]static double link:{java8-javadoc}/java/lang/Math.html#max%2Ddouble%2Ddouble%2D[max](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#max%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-min-2]]static double link:{java8-javadoc}/java/lang/Math.html#min%2Ddouble%2Ddouble%2D[min](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#min%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-nextAfter-2]]static double link:{java8-javadoc}/java/lang/Math.html#nextAfter%2Ddouble%2Ddouble%2D[nextAfter](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#nextAfter%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-nextDown-1]]static double link:{java8-javadoc}/java/lang/Math.html#nextDown%2Ddouble%2D[nextDown](double)++ (link:{java9-javadoc}/java/lang/Math.html#nextDown%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-nextUp-1]]static double link:{java8-javadoc}/java/lang/Math.html#nextUp%2Ddouble%2D[nextUp](double)++ (link:{java9-javadoc}/java/lang/Math.html#nextUp%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-pow-2]]static double link:{java8-javadoc}/java/lang/Math.html#pow%2Ddouble%2Ddouble%2D[pow](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#pow%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-random-0]]static double link:{java8-javadoc}/java/lang/Math.html#random%2D%2D[random]()++ (link:{java9-javadoc}/java/lang/Math.html#random%2D%2D[java 9]) +* ++[[painless-api-reference-Math-rint-1]]static double link:{java8-javadoc}/java/lang/Math.html#rint%2Ddouble%2D[rint](double)++ (link:{java9-javadoc}/java/lang/Math.html#rint%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-round-1]]static long link:{java8-javadoc}/java/lang/Math.html#round%2Ddouble%2D[round](double)++ (link:{java9-javadoc}/java/lang/Math.html#round%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-scalb-2]]static double link:{java8-javadoc}/java/lang/Math.html#scalb%2Ddouble%2Dint%2D[scalb](double, int)++ (link:{java9-javadoc}/java/lang/Math.html#scalb%2Ddouble%2Dint%2D[java 9]) +* ++[[painless-api-reference-Math-signum-1]]static double link:{java8-javadoc}/java/lang/Math.html#signum%2Ddouble%2D[signum](double)++ (link:{java9-javadoc}/java/lang/Math.html#signum%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-sin-1]]static double link:{java8-javadoc}/java/lang/Math.html#sin%2Ddouble%2D[sin](double)++ (link:{java9-javadoc}/java/lang/Math.html#sin%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-sinh-1]]static double link:{java8-javadoc}/java/lang/Math.html#sinh%2Ddouble%2D[sinh](double)++ (link:{java9-javadoc}/java/lang/Math.html#sinh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-sqrt-1]]static double link:{java8-javadoc}/java/lang/Math.html#sqrt%2Ddouble%2D[sqrt](double)++ (link:{java9-javadoc}/java/lang/Math.html#sqrt%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-tan-1]]static double link:{java8-javadoc}/java/lang/Math.html#tan%2Ddouble%2D[tan](double)++ (link:{java9-javadoc}/java/lang/Math.html#tan%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-tanh-1]]static double link:{java8-javadoc}/java/lang/Math.html#tanh%2Ddouble%2D[tanh](double)++ (link:{java9-javadoc}/java/lang/Math.html#tanh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-toDegrees-1]]static double link:{java8-javadoc}/java/lang/Math.html#toDegrees%2Ddouble%2D[toDegrees](double)++ (link:{java9-javadoc}/java/lang/Math.html#toDegrees%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-toRadians-1]]static double link:{java8-javadoc}/java/lang/Math.html#toRadians%2Ddouble%2D[toRadians](double)++ (link:{java9-javadoc}/java/lang/Math.html#toRadians%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-ulp-1]]static double link:{java8-javadoc}/java/lang/Math.html#ulp%2Ddouble%2D[ulp](double)++ (link:{java9-javadoc}/java/lang/Math.html#ulp%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/index.asciidoc b/docs/reference/painless-api-reference/index.asciidoc new file mode 100644 index 00000000000..51e62715484 --- /dev/null +++ b/docs/reference/painless-api-reference/index.asciidoc @@ -0,0 +1,338 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +include::AbstractChronology.asciidoc[] +include::AbstractCollection.asciidoc[] +include::AbstractList.asciidoc[] +include::AbstractMap.asciidoc[] +include::AbstractMap.SimpleEntry.asciidoc[] +include::AbstractMap.SimpleImmutableEntry.asciidoc[] +include::AbstractQueue.asciidoc[] +include::AbstractSequentialList.asciidoc[] +include::AbstractSet.asciidoc[] +include::Annotation.asciidoc[] +include::Appendable.asciidoc[] +include::ArithmeticException.asciidoc[] +include::ArrayDeque.asciidoc[] +include::ArrayIndexOutOfBoundsException.asciidoc[] +include::ArrayList.asciidoc[] +include::ArrayStoreException.asciidoc[] +include::Arrays.asciidoc[] +include::AttributedCharacterIterator.asciidoc[] +include::AttributedCharacterIterator.Attribute.asciidoc[] +include::AttributedString.asciidoc[] +include::Base64.asciidoc[] +include::Base64.Decoder.asciidoc[] +include::Base64.Encoder.asciidoc[] +include::BaseStream.asciidoc[] +include::BiConsumer.asciidoc[] +include::BiFunction.asciidoc[] +include::BiPredicate.asciidoc[] +include::Bidi.asciidoc[] +include::BigDecimal.asciidoc[] +include::BigInteger.asciidoc[] +include::BinaryOperator.asciidoc[] +include::BitSet.asciidoc[] +include::Boolean.asciidoc[] +include::BooleanSupplier.asciidoc[] +include::BreakIterator.asciidoc[] +include::Byte.asciidoc[] +include::BytesRef.asciidoc[] +include::Calendar.asciidoc[] +include::Calendar.Builder.asciidoc[] +include::CharSequence.asciidoc[] +include::Character.asciidoc[] +include::Character.Subset.asciidoc[] +include::Character.UnicodeBlock.asciidoc[] +include::Character.UnicodeScript.asciidoc[] +include::CharacterIterator.asciidoc[] +include::ChoiceFormat.asciidoc[] +include::ChronoField.asciidoc[] +include::ChronoLocalDate.asciidoc[] +include::ChronoLocalDateTime.asciidoc[] +include::ChronoPeriod.asciidoc[] +include::ChronoUnit.asciidoc[] +include::ChronoZonedDateTime.asciidoc[] +include::Chronology.asciidoc[] +include::ClassCastException.asciidoc[] +include::ClassNotFoundException.asciidoc[] +include::Clock.asciidoc[] +include::CloneNotSupportedException.asciidoc[] +include::CollationElementIterator.asciidoc[] +include::CollationKey.asciidoc[] +include::Collator.asciidoc[] +include::Collection.asciidoc[] +include::Collections.asciidoc[] +include::Collector.asciidoc[] +include::Collector.Characteristics.asciidoc[] +include::Collectors.asciidoc[] +include::Comparable.asciidoc[] +include::Comparator.asciidoc[] +include::ConcurrentModificationException.asciidoc[] +include::Consumer.asciidoc[] +include::Currency.asciidoc[] +include::Date.asciidoc[] +include::DateFormat.asciidoc[] +include::DateFormat.Field.asciidoc[] +include::DateFormatSymbols.asciidoc[] +include::DateTimeException.asciidoc[] +include::DateTimeFormatter.asciidoc[] +include::DateTimeFormatterBuilder.asciidoc[] +include::DateTimeParseException.asciidoc[] +include::DayOfWeek.asciidoc[] +include::Debug.asciidoc[] +include::DecimalFormat.asciidoc[] +include::DecimalFormatSymbols.asciidoc[] +include::DecimalStyle.asciidoc[] +include::Deque.asciidoc[] +include::Dictionary.asciidoc[] +include::Double.asciidoc[] +include::DoubleBinaryOperator.asciidoc[] +include::DoubleConsumer.asciidoc[] +include::DoubleFunction.asciidoc[] +include::DoublePredicate.asciidoc[] +include::DoubleStream.asciidoc[] +include::DoubleStream.Builder.asciidoc[] +include::DoubleSummaryStatistics.asciidoc[] +include::DoubleSupplier.asciidoc[] +include::DoubleToIntFunction.asciidoc[] +include::DoubleToLongFunction.asciidoc[] +include::DoubleUnaryOperator.asciidoc[] +include::DuplicateFormatFlagsException.asciidoc[] +include::Duration.asciidoc[] +include::EmptyStackException.asciidoc[] +include::Enum.asciidoc[] +include::EnumConstantNotPresentException.asciidoc[] +include::Enumeration.asciidoc[] +include::Era.asciidoc[] +include::EventListener.asciidoc[] +include::EventListenerProxy.asciidoc[] +include::EventObject.asciidoc[] +include::Exception.asciidoc[] +include::FieldPosition.asciidoc[] +include::Float.asciidoc[] +include::Format.asciidoc[] +include::Format.Field.asciidoc[] +include::FormatFlagsConversionMismatchException.asciidoc[] +include::FormatStyle.asciidoc[] +include::Formattable.asciidoc[] +include::FormattableFlags.asciidoc[] +include::Formatter.asciidoc[] +include::Formatter.BigDecimalLayoutForm.asciidoc[] +include::FormatterClosedException.asciidoc[] +include::Function.asciidoc[] +include::GregorianCalendar.asciidoc[] +include::HashMap.asciidoc[] +include::HashSet.asciidoc[] +include::Hashtable.asciidoc[] +include::HijrahChronology.asciidoc[] +include::HijrahDate.asciidoc[] +include::HijrahEra.asciidoc[] +include::IdentityHashMap.asciidoc[] +include::IllegalAccessException.asciidoc[] +include::IllegalArgumentException.asciidoc[] +include::IllegalFormatCodePointException.asciidoc[] +include::IllegalFormatConversionException.asciidoc[] +include::IllegalFormatException.asciidoc[] +include::IllegalFormatFlagsException.asciidoc[] +include::IllegalFormatPrecisionException.asciidoc[] +include::IllegalFormatWidthException.asciidoc[] +include::IllegalMonitorStateException.asciidoc[] +include::IllegalStateException.asciidoc[] +include::IllegalThreadStateException.asciidoc[] +include::IllformedLocaleException.asciidoc[] +include::IndexOutOfBoundsException.asciidoc[] +include::InputMismatchException.asciidoc[] +include::Instant.asciidoc[] +include::InstantiationException.asciidoc[] +include::IntBinaryOperator.asciidoc[] +include::IntConsumer.asciidoc[] +include::IntFunction.asciidoc[] +include::IntPredicate.asciidoc[] +include::IntStream.asciidoc[] +include::IntStream.Builder.asciidoc[] +include::IntSummaryStatistics.asciidoc[] +include::IntSupplier.asciidoc[] +include::IntToDoubleFunction.asciidoc[] +include::IntToLongFunction.asciidoc[] +include::IntUnaryOperator.asciidoc[] +include::Integer.asciidoc[] +include::InterruptedException.asciidoc[] +include::IsoChronology.asciidoc[] +include::IsoEra.asciidoc[] +include::IsoFields.asciidoc[] +include::Iterable.asciidoc[] +include::Iterator.asciidoc[] +include::JapaneseChronology.asciidoc[] +include::JapaneseDate.asciidoc[] +include::JapaneseEra.asciidoc[] +include::JulianFields.asciidoc[] +include::LinkedHashMap.asciidoc[] +include::LinkedHashSet.asciidoc[] +include::LinkedList.asciidoc[] +include::List.asciidoc[] +include::ListIterator.asciidoc[] +include::LocalDate.asciidoc[] +include::LocalDateTime.asciidoc[] +include::LocalTime.asciidoc[] +include::Locale.asciidoc[] +include::Locale.Builder.asciidoc[] +include::Locale.Category.asciidoc[] +include::Locale.FilteringMode.asciidoc[] +include::Locale.LanguageRange.asciidoc[] +include::Long.asciidoc[] +include::LongBinaryOperator.asciidoc[] +include::LongConsumer.asciidoc[] +include::LongFunction.asciidoc[] +include::LongPredicate.asciidoc[] +include::LongStream.asciidoc[] +include::LongStream.Builder.asciidoc[] +include::LongSummaryStatistics.asciidoc[] +include::LongSupplier.asciidoc[] +include::LongToDoubleFunction.asciidoc[] +include::LongToIntFunction.asciidoc[] +include::LongUnaryOperator.asciidoc[] +include::Map.asciidoc[] +include::Map.Entry.asciidoc[] +include::Matcher.asciidoc[] +include::Math.asciidoc[] +include::MathContext.asciidoc[] +include::MessageFormat.asciidoc[] +include::MessageFormat.Field.asciidoc[] +include::MinguoChronology.asciidoc[] +include::MinguoDate.asciidoc[] +include::MinguoEra.asciidoc[] +include::MissingFormatArgumentException.asciidoc[] +include::MissingFormatWidthException.asciidoc[] +include::MissingResourceException.asciidoc[] +include::Month.asciidoc[] +include::MonthDay.asciidoc[] +include::NavigableMap.asciidoc[] +include::NavigableSet.asciidoc[] +include::NegativeArraySizeException.asciidoc[] +include::NoSuchElementException.asciidoc[] +include::NoSuchFieldException.asciidoc[] +include::NoSuchMethodException.asciidoc[] +include::Normalizer.asciidoc[] +include::Normalizer.Form.asciidoc[] +include::NullPointerException.asciidoc[] +include::Number.asciidoc[] +include::NumberFormat.asciidoc[] +include::NumberFormat.Field.asciidoc[] +include::NumberFormatException.asciidoc[] +include::ObjDoubleConsumer.asciidoc[] +include::ObjIntConsumer.asciidoc[] +include::ObjLongConsumer.asciidoc[] +include::Object.asciidoc[] +include::Objects.asciidoc[] +include::Observable.asciidoc[] +include::Observer.asciidoc[] +include::OffsetDateTime.asciidoc[] +include::OffsetTime.asciidoc[] +include::Optional.asciidoc[] +include::OptionalDouble.asciidoc[] +include::OptionalInt.asciidoc[] +include::OptionalLong.asciidoc[] +include::ParseException.asciidoc[] +include::ParsePosition.asciidoc[] +include::Pattern.asciidoc[] +include::Period.asciidoc[] +include::Predicate.asciidoc[] +include::PrimitiveIterator.asciidoc[] +include::PrimitiveIterator.OfDouble.asciidoc[] +include::PrimitiveIterator.OfInt.asciidoc[] +include::PrimitiveIterator.OfLong.asciidoc[] +include::PriorityQueue.asciidoc[] +include::Queue.asciidoc[] +include::Random.asciidoc[] +include::RandomAccess.asciidoc[] +include::ReflectiveOperationException.asciidoc[] +include::ResolverStyle.asciidoc[] +include::RoundingMode.asciidoc[] +include::RuleBasedCollator.asciidoc[] +include::RuntimeException.asciidoc[] +include::SecurityException.asciidoc[] +include::Set.asciidoc[] +include::Short.asciidoc[] +include::SignStyle.asciidoc[] +include::SimpleDateFormat.asciidoc[] +include::SimpleTimeZone.asciidoc[] +include::SortedMap.asciidoc[] +include::SortedSet.asciidoc[] +include::Spliterator.asciidoc[] +include::Spliterator.OfDouble.asciidoc[] +include::Spliterator.OfInt.asciidoc[] +include::Spliterator.OfLong.asciidoc[] +include::Spliterator.OfPrimitive.asciidoc[] +include::Spliterators.asciidoc[] +include::Stack.asciidoc[] +include::StackTraceElement.asciidoc[] +include::Stream.asciidoc[] +include::Stream.Builder.asciidoc[] +include::StrictMath.asciidoc[] +include::String.asciidoc[] +include::StringBuffer.asciidoc[] +include::StringBuilder.asciidoc[] +include::StringCharacterIterator.asciidoc[] +include::StringIndexOutOfBoundsException.asciidoc[] +include::StringJoiner.asciidoc[] +include::StringTokenizer.asciidoc[] +include::Supplier.asciidoc[] +include::System.asciidoc[] +include::Temporal.asciidoc[] +include::TemporalAccessor.asciidoc[] +include::TemporalAdjuster.asciidoc[] +include::TemporalAdjusters.asciidoc[] +include::TemporalAmount.asciidoc[] +include::TemporalField.asciidoc[] +include::TemporalQueries.asciidoc[] +include::TemporalQuery.asciidoc[] +include::TemporalUnit.asciidoc[] +include::TextStyle.asciidoc[] +include::ThaiBuddhistChronology.asciidoc[] +include::ThaiBuddhistDate.asciidoc[] +include::ThaiBuddhistEra.asciidoc[] +include::TimeZone.asciidoc[] +include::ToDoubleBiFunction.asciidoc[] +include::ToDoubleFunction.asciidoc[] +include::ToIntBiFunction.asciidoc[] +include::ToIntFunction.asciidoc[] +include::ToLongBiFunction.asciidoc[] +include::ToLongFunction.asciidoc[] +include::TooManyListenersException.asciidoc[] +include::TreeMap.asciidoc[] +include::TreeSet.asciidoc[] +include::TypeNotPresentException.asciidoc[] +include::UUID.asciidoc[] +include::UnaryOperator.asciidoc[] +include::UnknownFormatConversionException.asciidoc[] +include::UnknownFormatFlagsException.asciidoc[] +include::UnsupportedOperationException.asciidoc[] +include::UnsupportedTemporalTypeException.asciidoc[] +include::ValueRange.asciidoc[] +include::Vector.asciidoc[] +include::WeekFields.asciidoc[] +include::Year.asciidoc[] +include::YearMonth.asciidoc[] +include::ZoneId.asciidoc[] +include::ZoneOffset.asciidoc[] +include::ZoneOffsetTransition.asciidoc[] +include::ZoneOffsetTransitionRule.asciidoc[] +include::ZoneOffsetTransitionRule.TimeDefinition.asciidoc[] +include::ZoneRules.asciidoc[] +include::ZoneRulesException.asciidoc[] +include::ZoneRulesProvider.asciidoc[] +include::ZonedDateTime.asciidoc[] +include::org.elasticsearch.common.geo.GeoPoint.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc[] +include::org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc[] +include::org.elasticsearch.painless.FeatureTest.asciidoc[] +include::org.joda.time.ReadableDateTime.asciidoc[] +include::org.joda.time.ReadableInstant.asciidoc[] diff --git a/modules/lang-painless/build.gradle b/modules/lang-painless/build.gradle index f93ea417572..dc56039afee 100644 --- a/modules/lang-painless/build.gradle +++ b/modules/lang-painless/build.gradle @@ -53,7 +53,8 @@ integTest { } } -// Build +/* Build Javadoc for the Java classes in Painless's public API that are in the + * Painless plugin */ task apiJavadoc(type: Javadoc) { source = sourceSets.main.allJava include '**/org/elasticsearch/painless/api/' @@ -64,3 +65,10 @@ task apiJavadocJar(type: Jar) { from apiJavadoc } assemble.dependsOn apiJavadocJar + +// Reference documentation for Painless's public API. +task generatePainlessApi(type: JavaExec) { + main = 'org.elasticsearch.painless.PainlessDocGenerator' + classpath = sourceSets.test.runtimeClasspath + args file('../../docs/reference/painless-api-reference') +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Debug.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Debug.java deleted file mode 100644 index 29861000e1b..00000000000 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Debug.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.painless; - -import org.elasticsearch.script.ScriptException; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.TreeMap; - -import static java.util.Collections.singletonList; - -/** - * Utility methods for debugging painless scripts that are accessible to painless scripts. - */ -public class Debug { - private Debug() {} - - /** - * Throw an {@link Error} that "explains" an object. - */ - public static void explain(Object objectToExplain) throws PainlessExplainError { - throw new PainlessExplainError(objectToExplain); - } - - /** - * Thrown by {@link Debug#explain(Object)} to explain an object. Subclass of {@linkplain Error} so it cannot be caught by painless - * scripts. - */ - public static class PainlessExplainError extends Error { - private final Object objectToExplain; - - public PainlessExplainError(Object objectToExplain) { - this.objectToExplain = objectToExplain; - } - - Object getObjectToExplain() { - return objectToExplain; - } - - /** - * Headers to be added to the {@link ScriptException} for structured rendering. - */ - Map> getMetadata() { - Map> metadata = new TreeMap<>(); - metadata.put("es.class", singletonList(objectToExplain == null ? "null" : objectToExplain.getClass().getName())); - metadata.put("es.to_string", singletonList(Objects.toString(objectToExplain))); - return metadata; - } - } -} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java index d15e409c834..9f03540c2d8 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java @@ -34,6 +34,7 @@ import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -462,16 +463,22 @@ public final class Definition { } public static final class RuntimeClass { + private final Struct struct; public final Map methods; public final Map getters; public final Map setters; - private RuntimeClass(final Map methods, + private RuntimeClass(final Struct struct, final Map methods, final Map getters, final Map setters) { + this.struct = struct; this.methods = Collections.unmodifiableMap(methods); this.getters = Collections.unmodifiableMap(getters); this.setters = Collections.unmodifiableMap(setters); } + + public Struct getStruct() { + return struct; + } } /** Returns whether or not a non-array type exists. */ @@ -504,6 +511,11 @@ public final class Definition { return INSTANCE.runtimeMap.get(clazz); } + /** Collection of all simple types. Used by {@code PainlessDocGenerator} to generate an API reference. */ + static Collection allSimpleTypes() { + return INSTANCE.simpleTypesMap.values(); + } + // INTERNAL IMPLEMENTATION: private final Map, RuntimeClass> runtimeMap; @@ -1048,7 +1060,7 @@ public final class Definition { } } - runtimeMap.put(struct.clazz, new RuntimeClass(methods, getters, setters)); + runtimeMap.put(struct.clazz, new RuntimeClass(struct, methods, getters, setters)); } /** computes the functional interface method for a class, or returns null */ diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java new file mode 100644 index 00000000000..75a1d2392cd --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java @@ -0,0 +1,72 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.painless; + +import org.elasticsearch.painless.api.Debug; +import org.elasticsearch.script.ScriptException; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import static java.util.Collections.singletonList; + +/** + * Thrown by {@link Debug#explain(Object)} to explain an object. Subclass of {@linkplain Error} so it cannot be caught by painless + * scripts. + */ +public class PainlessExplainError extends Error { + private final Object objectToExplain; + + public PainlessExplainError(Object objectToExplain) { + this.objectToExplain = objectToExplain; + } + + Object getObjectToExplain() { + return objectToExplain; + } + + /** + * Headers to be added to the {@link ScriptException} for structured rendering. + */ + Map> getHeaders() { + Map> headers = new TreeMap<>(); + String toString = "null"; + String javaClassName = null; + String painlessClassName = null; + if (objectToExplain != null) { + toString = objectToExplain.toString(); + javaClassName = objectToExplain.getClass().getName(); + Definition.RuntimeClass runtimeClass = Definition.getRuntimeClass(objectToExplain.getClass()); + if (runtimeClass != null) { + painlessClassName = runtimeClass.getStruct().name; + } + } + + headers.put("es.to_string", singletonList(toString)); + if (painlessClassName != null) { + headers.put("es.painless_class", singletonList(painlessClassName)); + } + if (javaClassName != null) { + headers.put("es.java_class", singletonList(javaClassName)); + } + return headers; + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java index 76ae31ce426..7af6a65d251 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java @@ -121,9 +121,9 @@ final class ScriptImpl implements ExecutableScript, LeafSearchScript { public Object run() { try { return executable.execute(variables, scorer, doc, aggregationValue); - // Note that it is safe to catch any of the following errors since Painless is stateless. - } catch (Debug.PainlessExplainError e) { - throw convertToScriptException(e, e.getMetadata()); + } catch (PainlessExplainError e) { + throw convertToScriptException(e, e.getHeaders()); + // Note that it is safe to catch any of the following errors since Painless is stateless. } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { throw convertToScriptException(e, emptyMap()); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/api/Debug.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/api/Debug.java new file mode 100644 index 00000000000..de0c9b3adbd --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/api/Debug.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.painless.api; + +import org.elasticsearch.painless.PainlessExplainError; + +/** + * Utility methods for debugging painless scripts that are accessible to painless scripts. + */ +public class Debug { + private Debug() {} + + /** + * Throw an {@link Error} that "explains" an object. + */ + public static void explain(Object objectToExplain) throws PainlessExplainError { + throw new PainlessExplainError(objectToExplain); + } +} diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt index 92eccc5ac3c..42cc59e4a23 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt @@ -60,7 +60,7 @@ class def -> java.lang.Object { #### Painless debugging API -class Debug -> org.elasticsearch.painless.Debug extends Object { +class Debug -> org.elasticsearch.painless.api.Debug extends Object { void explain(Object) } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DebugTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DebugTests.java index 1b2eb25c491..0c96251a51a 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DebugTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DebugTests.java @@ -30,49 +30,54 @@ import java.util.Map; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.not; public class DebugTests extends ScriptTestCase { public void testExplain() { + // Debug.explain can explain an object Object dummy = new Object(); - Map params = singletonMap("a", dummy); - - Debug.PainlessExplainError e = expectScriptThrows(Debug.PainlessExplainError.class, () -> exec( - "Debug.explain(params.a)", params, true)); + PainlessExplainError e = expectScriptThrows(PainlessExplainError.class, () -> exec( + "Debug.explain(params.a)", singletonMap("a", dummy), true)); assertSame(dummy, e.getObjectToExplain()); - assertThat(e.getMetadata(), hasEntry("es.class", singletonList("java.lang.Object"))); - assertThat(e.getMetadata(), hasEntry("es.to_string", singletonList(dummy.toString()))); + assertThat(e.getHeaders(), hasEntry("es.to_string", singletonList(dummy.toString()))); + assertThat(e.getHeaders(), hasEntry("es.java_class", singletonList("java.lang.Object"))); + assertThat(e.getHeaders(), hasEntry("es.painless_class", singletonList("Object"))); // Null should be ok - e = expectScriptThrows(Debug.PainlessExplainError.class, () -> exec("Debug.explain(null)")); + e = expectScriptThrows(PainlessExplainError.class, () -> exec("Debug.explain(null)")); assertNull(e.getObjectToExplain()); - assertThat(e.getMetadata(), hasEntry("es.class", singletonList("null"))); - assertThat(e.getMetadata(), hasEntry("es.to_string", singletonList("null"))); + assertThat(e.getHeaders(), hasEntry("es.to_string", singletonList("null"))); + assertThat(e.getHeaders(), not(hasKey("es.java_class"))); + assertThat(e.getHeaders(), not(hasKey("es.painless_class"))); // You can't catch the explain exception - e = expectScriptThrows(Debug.PainlessExplainError.class, () -> exec( + e = expectScriptThrows(PainlessExplainError.class, () -> exec( "try {\n" + " Debug.explain(params.a)\n" + "} catch (Exception e) {\n" + " return 1\n" - + "}", params, true)); + + "}", singletonMap("a", dummy), true)); assertSame(dummy, e.getObjectToExplain()); } /** - * {@link Debug.PainlessExplainError} doesn't serialize but the headers still make it. + * {@link PainlessExplainError} doesn't serialize but the headers still make it. */ public void testPainlessExplainErrorSerialization() throws IOException { Map params = singletonMap("a", "jumped over the moon"); ScriptException e = expectThrows(ScriptException.class, () -> exec("Debug.explain(params.a)", params, true)); - assertEquals(singletonList("java.lang.String"), e.getMetadata("es.class")); assertEquals(singletonList("jumped over the moon"), e.getMetadata("es.to_string")); + assertEquals(singletonList("java.lang.String"), e.getMetadata("es.java_class")); + assertEquals(singletonList("String"), e.getMetadata("es.painless_class")); try (BytesStreamOutput out = new BytesStreamOutput()) { out.writeException(e); try (StreamInput in = out.bytes().streamInput()) { ElasticsearchException read = (ScriptException) in.readException(); - assertEquals(singletonList("java.lang.String"), read.getMetadata("es.class")); assertEquals(singletonList("jumped over the moon"), read.getMetadata("es.to_string")); + assertEquals(singletonList("java.lang.String"), read.getMetadata("es.java_class")); + assertEquals(singletonList("String"), read.getMetadata("es.painless_class")); } } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java new file mode 100644 index 00000000000..910c4940ab5 --- /dev/null +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java @@ -0,0 +1,355 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.painless; + +import org.apache.logging.log4j.Logger; +import org.apache.lucene.util.IOUtils; +import org.elasticsearch.common.io.PathUtils; +import org.elasticsearch.common.logging.ESLoggerFactory; +import org.elasticsearch.painless.Definition.Field; +import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.Definition.Struct; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.api.Augmentation; + +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Consumer; + +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; + +/** + * Generates an API reference from the method and type whitelists in {@link Definition}. + */ +public class PainlessDocGenerator { + private static final Logger logger = ESLoggerFactory.getLogger(PainlessDocGenerator.class); + private static final Comparator FIELD_NAME = comparing(f -> f.name); + private static final Comparator METHOD_NAME = comparing(m -> m.name); + private static final Comparator NUMBER_OF_ARGS = comparing(m -> m.arguments.size()); + + public static void main(String[] args) throws IOException { + Path apiRootPath = PathUtils.get(args[0]); + + // Blow away the last execution and recreate it from scratch + IOUtils.rm(apiRootPath); + Files.createDirectories(apiRootPath); + + Path indexPath = apiRootPath.resolve("index.asciidoc"); + logger.info("Starting to write [index.asciidoc]"); + try (PrintStream indexStream = new PrintStream( + Files.newOutputStream(indexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE), + false, StandardCharsets.UTF_8.name())) { + emitGeneratedWarning(indexStream); + List types = Definition.allSimpleTypes().stream().sorted(comparing(t -> t.name)).collect(toList()); + for (Type type : types) { + if (type.sort.primitive) { + // Primitives don't have methods to reference + continue; + } + if ("def".equals(type.name)) { + // def is special but doesn't have any methods all of its own. + continue; + } + indexStream.print("include::"); + indexStream.print(type.struct.name); + indexStream.println(".asciidoc[]"); + + Path typePath = apiRootPath.resolve(type.struct.name + ".asciidoc"); + logger.info("Writing [{}.asciidoc]", type.name); + try (PrintStream typeStream = new PrintStream( + Files.newOutputStream(typePath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE), + false, StandardCharsets.UTF_8.name())) { + emitGeneratedWarning(typeStream); + typeStream.print("[["); + emitAnchor(typeStream, type.struct); + typeStream.print("]]++"); + typeStream.print(type.name); + typeStream.println("++::"); + + Consumer documentField = field -> PainlessDocGenerator.documentField(typeStream, field); + Consumer documentMethod = method -> PainlessDocGenerator.documentMethod(typeStream, method); + type.struct.staticMembers.values().stream().sorted(FIELD_NAME).forEach(documentField); + type.struct.members.values().stream().sorted(FIELD_NAME).forEach(documentField); + type.struct.staticMethods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(documentMethod); + type.struct.constructors.values().stream().sorted(NUMBER_OF_ARGS).forEach(documentMethod); + Map inherited = new TreeMap<>(); + type.struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(method -> { + if (method.owner == type.struct) { + documentMethod(typeStream, method); + } else { + inherited.put(method.owner.name, method.owner); + } + }); + + if (false == inherited.isEmpty()) { + typeStream.print("* Inherits methods from "); + boolean first = true; + for (Struct inheritsFrom : inherited.values()) { + if (first) { + first = false; + } else { + typeStream.print(", "); + } + typeStream.print("++"); + emitStruct(typeStream, inheritsFrom); + typeStream.print("++"); + } + typeStream.println(); + } + } + } + } + logger.info("Done writing [index.asciidoc]"); + } + + private static void documentField(PrintStream stream, Field field) { + stream.print("** [["); + emitAnchor(stream, field); + stream.print("]]"); + + if (Modifier.isStatic(field.modifiers)) { + stream.print("static "); + } + + emitType(stream, field.type); + stream.print(' '); + + String javadocRoot = javadocRoot(field); + emitJavadocLink(stream, javadocRoot, field); + stream.print('['); + stream.print(field.name); + stream.print(']'); + + if (javadocRoot.equals("java8")) { + stream.print(" ("); + emitJavadocLink(stream, "java9", field); + stream.print("[java 9])"); + } + + stream.println(); + } + + /** + * Document a method. + */ + private static void documentMethod(PrintStream stream, Method method) { + stream.print("* ++[["); + emitAnchor(stream, method); + stream.print("]]"); + + if (false == method.augmentation && Modifier.isStatic(method.modifiers)) { + stream.print("static "); + } + + if (false == method.name.equals("")) { + emitType(stream, method.rtn); + stream.print(' '); + } + + String javadocRoot = javadocRoot(method); + emitJavadocLink(stream, javadocRoot, method); + stream.print('['); + + stream.print(methodName(method)); + + stream.print("]("); + boolean first = true; + for (Type arg : method.arguments) { + if (first) { + first = false; + } else { + stream.print(", "); + } + emitType(stream, arg); + } + stream.print(")++"); + + if (javadocRoot.equals("java8")) { + stream.print(" ("); + emitJavadocLink(stream, "java9", method); + stream.print("[java 9])"); + } + + stream.println(); + } + + /** + * Anchor text for a {@link Struct}. + */ + private static void emitAnchor(PrintStream stream, Struct struct) { + stream.print("painless-api-reference-"); + stream.print(struct.name.replace('.', '-')); + } + + /** + * Anchor text for a {@link Method}. + */ + private static void emitAnchor(PrintStream stream, Method method) { + emitAnchor(stream, method.owner); + stream.print('-'); + stream.print(methodName(method)); + stream.print('-'); + stream.print(method.arguments.size()); + } + + /** + * Anchor text for a {@link Field}. + */ + private static void emitAnchor(PrintStream stream, Field field) { + emitAnchor(stream, field.owner); + stream.print('-'); + stream.print(field.name); + } + + private static String methodName(Method method) { + return method.name.equals("") ? method.owner.name : method.name; + } + + /** + * Emit a {@link Type}. If the type is primitive or an array of primitives this just emits the name of the type. Otherwise this emits an + * internal link with the text. + */ + private static void emitType(PrintStream stream, Type type) { + emitStruct(stream, type.struct); + for (int i = 0; i < type.dimensions; i++) { + stream.print("[]"); + } + } + + /** + * Emit a {@link Struct}. If the {@linkplain Struct} is primitive or def this just emits the name of the struct. Otherwise this emits an + * internal link with the name. + */ + private static void emitStruct(PrintStream stream, Struct struct) { + if (false == struct.clazz.isPrimitive() && false == struct.name.equals("def")) { + stream.print("<<"); + emitAnchor(stream, struct); + stream.print(','); + stream.print(struct.name); + stream.print(">>"); + } else { + stream.print(struct.name); + } + } + + /** + * Emit an external link to Javadoc for a {@link Method}. + * + * @param root name of the root uri variable + */ + private static void emitJavadocLink(PrintStream stream, String root, Method method) { + stream.print("link:{"); + stream.print(root); + stream.print("-javadoc}/"); + stream.print((method.augmentation ? Augmentation.class : method.owner.clazz).getName().replace('.', '/')); + stream.print(".html#"); + stream.print(methodName(method)); + stream.print("%2D"); + boolean first = true; + if (method.augmentation) { + first = false; + stream.print(method.owner.clazz.getName()); + } + for (Type arg: method.arguments) { + if (first) { + first = false; + } else { + stream.print("%2D"); + } + stream.print(arg.struct.clazz.getName()); + if (arg.dimensions > 0) { + stream.print(":A"); + } + } + stream.print("%2D"); + } + + /** + * Emit an external link to Javadoc for a {@link Field}. + * + * @param root name of the root uri variable + */ + private static void emitJavadocLink(PrintStream stream, String root, Field field) { + stream.print("link:{"); + stream.print(root); + stream.print("-javadoc}/"); + stream.print(field.owner.clazz.getName().replace('.', '/')); + stream.print(".html#"); + stream.print(field.javaName); + } + + /** + * Pick the javadoc root for a {@link Method}. + */ + private static String javadocRoot(Method method) { + if (method.augmentation) { + return "painless"; + } + return javadocRoot(method.owner); + } + + /** + * Pick the javadoc root for a {@link Field}. + */ + private static String javadocRoot(Field field) { + return javadocRoot(field.owner); + } + + /** + * Pick the javadoc root for a {@link Struct}. + */ + private static String javadocRoot(Struct struct) { + String classPackage = struct.clazz.getPackage().getName(); + if (classPackage.startsWith("java")) { + return "java8"; + } + if (classPackage.startsWith("org.elasticsearch.painless")) { + return "painless"; + } + if (classPackage.startsWith("org.elasticsearch")) { + return "elasticsearch"; + } + if (classPackage.startsWith("org.joda.time")) { + return "joda-time"; + } + if (classPackage.startsWith("org.apache.lucene")) { + return "lucene-core"; + } + throw new IllegalArgumentException("Unrecognized packge: " + classPackage); + } + + private static void emitGeneratedWarning(PrintStream stream) { + stream.println("////"); + stream.println("Automatically generated by PainlessDocGenerator. Do not edit."); + stream.println("Rebuild by running `gradle generatePainlessApi`."); + stream.println("////"); + stream.println(); + } +}