diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index 8d411abef2..da0c526a5b 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -47,7 +47,6 @@ import java.util.concurrent.TimeUnit; import processing.opengl.PGL; -import processing.opengl.PShader; /** * diff --git a/core/src/processing/core/PShader.java b/core/src/processing/core/PShader.java new file mode 100644 index 0000000000..4882a0952c --- /dev/null +++ b/core/src/processing/core/PShader.java @@ -0,0 +1,113 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-21 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.core; + +/** + * This class encapsulates a shader program, including a vertex and a fragment + * shader. It is the renderer-agnostic type that sketches declare and pass to + * shader() and filter(); each renderer supplies its own + * implementation (for example a GLSL-backed shader under P2D/P3D, or a WGSL + * one under the WebGPU renderer). + * + * Use the loadShader() function to load your shader code, rather than + * constructing a renderer-specific implementation directly. + * + * @webref rendering:shaders + * @webBrief This class encapsulates a shader program, including a vertex and a + * fragment shader + */ +public interface PShader { + + /** + * Sets the uniform variables inside the shader to modify the effect while the + * program is running. + * + * @webref rendering:shaders + * @webBrief Sets a variable within the shader + * @param name the name of the uniform variable to modify + * @param x first component of the variable to modify + */ + void set(String name, int x); + + /** + * @param y second component of the variable to modify. The variable has to be declared with an array/vector type in the shader (i.e.: int[2], vec2) + */ + void set(String name, int x, int y); + + /** + * @param z third component of the variable to modify. The variable has to be declared with an array/vector type in the shader (i.e.: int[3], vec3) + */ + void set(String name, int x, int y, int z); + + /** + * @param w fourth component of the variable to modify. The variable has to be declared with an array/vector type in the shader (i.e.: int[4], vec4) + */ + void set(String name, int x, int y, int z, int w); + + void set(String name, float x); + void set(String name, float x, float y); + void set(String name, float x, float y, float z); + void set(String name, float x, float y, float z, float w); + + /** + * @param vec modifies all the components of an array/vector uniform variable. PVector can only be used if the type of the variable is vec3. + */ + void set(String name, PVector vec); + + void set(String name, boolean x); + void set(String name, boolean x, boolean y); + void set(String name, boolean x, boolean y, boolean z); + void set(String name, boolean x, boolean y, boolean z, boolean w); + + void set(String name, int[] vec); + + /** + * @param ncoords number of coordinates per element, max 4 + */ + void set(String name, int[] vec, int ncoords); + + void set(String name, float[] vec); + void set(String name, float[] vec, int ncoords); + + void set(String name, boolean[] vec); + void set(String name, boolean[] boolvec, int ncoords); + + /** + * @param mat matrix of values + */ + void set(String name, PMatrix2D mat); + + void set(String name, PMatrix3D mat); + + /** + * @param use3x3 enforces the matrix is 3 x 3 + */ + void set(String name, PMatrix3D mat, boolean use3x3); + + /** + * @param tex sets the sampler uniform variable to read from this image texture + */ + void set(String name, PImage tex); +} diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index 88164f43e1..5d592b8db9 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -204,17 +204,17 @@ public void dispose() { static protected URL maskShaderFragURL = PGraphicsOpenGL.class.getResource("/processing/opengl/shaders/MaskFrag.glsl"); - protected PShader defColorShader; - protected PShader defTextureShader; - protected PShader defLightShader; - protected PShader defTexlightShader; - protected PShader defLineShader; - protected PShader defPointShader; - protected PShader maskShader; - - protected PShader polyShader; - protected PShader lineShader; - protected PShader pointShader; + protected PShaderOpenGL defColorShader; + protected PShaderOpenGL defTextureShader; + protected PShaderOpenGL defLightShader; + protected PShaderOpenGL defTexlightShader; + protected PShaderOpenGL defLineShader; + protected PShaderOpenGL defPointShader; + protected PShaderOpenGL maskShader; + + protected PShaderOpenGL polyShader; + protected PShaderOpenGL lineShader; + protected PShaderOpenGL pointShader; // ........................................................ @@ -984,7 +984,7 @@ public int hashCode() { } - protected static class GLResourceShader extends Disposable { + protected static class GLResourceShader extends Disposable { int glProgram; int glVertex; int glFragment; @@ -992,7 +992,7 @@ protected static class GLResourceShader extends Disposable { private PGL pgl; final private int context; - public GLResourceShader(PShader sh) { + public GLResourceShader(PShaderOpenGL sh) { super(sh); this.pgl = sh.pgl.graphics.getPrimaryPGL(); @@ -2341,7 +2341,7 @@ protected void flushPolys() { // If the renderer is 2D, then lights should always be false, // so no need to worry about that. - PShader shader = getPolyShader(lights, tex != null); + PShaderOpenGL shader = getPolyShader(lights, tex != null); shader.bind(); int first = texCache.firstCache[i]; @@ -2442,7 +2442,7 @@ protected void flushSortedPolys() { // If the renderer is 2D, then lights should always be false, // so no need to worry about that. - PShader shader = getPolyShader(lights, tex != null); + PShaderOpenGL shader = getPolyShader(lights, tex != null); shader.bind(); shader.setVertexAttribute(bufPolyVertex.glId, 4, PGL.FLOAT, 0, @@ -2704,7 +2704,7 @@ void rawSortedPolys() { protected void flushLines() { updateLineBuffers(); - PShader shader = getLineShader(); + PShaderOpenGL shader = getLineShader(); shader.bind(); IndexCache cache = tessGeo.lineIndexCache; @@ -2804,7 +2804,7 @@ void rawLines() { protected void flushPoints() { updatePointBuffers(); - PShader shader = getPointShader(); + PShaderOpenGL shader = getPointShader(); shader.bind(); IndexCache cache = tessGeo.pointIndexCache; @@ -6002,7 +6002,7 @@ public void mask(PImage alpha) { PGraphicsOpenGL ppg = getPrimaryPG(); if (ppg.maskShader == null) { - ppg.maskShader = new PShader(parent, defTextureShaderVertURL, + ppg.maskShader = new PShaderOpenGL(parent, defTextureShaderVertURL, maskShaderFragURL); } ppg.maskShader.set("mask", alpha); @@ -6044,7 +6044,8 @@ public void filter(int kind, float param) { @Override public void filter(PShader shader) { - if (!shader.isPolyShader()) { + PShaderOpenGL glShader = (PShaderOpenGL) shader; + if (!glShader.isPolyShader()) { PGraphics.showWarning(INVALID_FILTER_SHADER_ERROR); return; } @@ -6087,8 +6088,8 @@ public void filter(PShader shader) { stroke = false; int prevBlendMode = blendMode; blendMode(REPLACE); - PShader prevShader = polyShader; - polyShader = shader; + PShaderOpenGL prevShader = polyShader; + polyShader = glShader; beginShape(QUADS); texture(filterImage); @@ -6910,27 +6911,27 @@ public PShader loadShader(String fragFilename) { return null; } - int type = PShader.getShaderType(parent.loadStrings(fragFilename), - PShader.POLY); - PShader shader = new PShader(parent); + int type = PShaderOpenGL.getShaderType(parent.loadStrings(fragFilename), + PShaderOpenGL.POLY); + PShaderOpenGL shader = new PShaderOpenGL(parent); shader.setType(type); shader.setFragmentShader(fragFilename); - if (type == PShader.POINT) { + if (type == PShaderOpenGL.POINT) { String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL); shader.setVertexShader(vertSource); - } else if (type == PShader.LINE) { + } else if (type == PShaderOpenGL.LINE) { String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL); shader.setVertexShader(vertSource); - } else if (type == PShader.TEXLIGHT) { + } else if (type == PShaderOpenGL.TEXLIGHT) { String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL); shader.setVertexShader(vertSource); - } else if (type == PShader.LIGHT) { + } else if (type == PShaderOpenGL.LIGHT) { String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL); shader.setVertexShader(vertSource); - } else if (type == PShader.TEXTURE) { + } else if (type == PShaderOpenGL.TEXTURE) { String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL); shader.setVertexShader(vertSource); - } else if (type == PShader.COLOR) { + } else if (type == PShaderOpenGL.COLOR) { String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL); shader.setVertexShader(vertSource); } else { @@ -6943,13 +6944,13 @@ public PShader loadShader(String fragFilename) { @Override public PShader loadShader(String fragFilename, String vertFilename) { - PShader shader = null; + PShaderOpenGL shader = null; if (fragFilename == null || fragFilename.equals("")) { PGraphics.showWarning(MISSING_FRAGMENT_SHADER); } else if (vertFilename == null || vertFilename.equals("")) { PGraphics.showWarning(MISSING_VERTEX_SHADER); } else { - shader = new PShader(parent, vertFilename, fragFilename); + shader = new PShaderOpenGL(parent, vertFilename, fragFilename); } return shader; } @@ -6957,24 +6958,26 @@ public PShader loadShader(String fragFilename, String vertFilename) { @Override public void shader(PShader shader) { + PShaderOpenGL glShader = (PShaderOpenGL) shader; flush(); // Flushing geometry drawn with a different shader. - if (shader != null) shader.init(); - if (shader.isPolyShader()) polyShader = shader; - else if (shader.isLineShader()) lineShader = shader; - else if (shader.isPointShader()) pointShader = shader; + if (glShader != null) glShader.init(); + if (glShader.isPolyShader()) polyShader = glShader; + else if (glShader.isLineShader()) lineShader = glShader; + else if (glShader.isPointShader()) pointShader = glShader; else PGraphics.showWarning(UNKNOWN_SHADER_KIND_ERROR); } @Override public void shader(PShader shader, int kind) { + PShaderOpenGL glShader = (PShaderOpenGL) shader; flush(); // Flushing geometry drawn with a different shader. - if (shader != null) shader.init(); - if (kind == TRIANGLES) polyShader = shader; - else if (kind == LINES) lineShader = shader; - else if (kind == POINTS) pointShader = shader; + if (glShader != null) glShader.init(); + if (kind == TRIANGLES) polyShader = glShader; + else if (kind == LINES) lineShader = glShader; + else if (kind == POINTS) pointShader = glShader; else PGraphics.showWarning(UNKNOWN_SHADER_KIND_ERROR); } @@ -7001,8 +7004,8 @@ public void resetShader(int kind) { } - protected PShader getPolyShader(boolean lit, boolean tex) { - PShader shader; + protected PShaderOpenGL getPolyShader(boolean lit, boolean tex) { + PShaderOpenGL shader; PGraphicsOpenGL ppg = getPrimaryPG(); boolean useDefault = polyShader == null; if (polyShader != null) { @@ -7012,22 +7015,22 @@ protected PShader getPolyShader(boolean lit, boolean tex) { } if (lit) { if (tex) { - if (useDefault || !polyShader.checkPolyType(PShader.TEXLIGHT)) { + if (useDefault || !polyShader.checkPolyType(PShaderOpenGL.TEXLIGHT)) { if (ppg.defTexlightShader == null) { String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL); String[] fragSource = pgl.loadFragmentShader(defTexlightShaderFragURL); - ppg.defTexlightShader = new PShader(parent, vertSource, fragSource); + ppg.defTexlightShader = new PShaderOpenGL(parent, vertSource, fragSource); } shader = ppg.defTexlightShader; } else { shader = polyShader; } } else { - if (useDefault || !polyShader.checkPolyType(PShader.LIGHT)) { + if (useDefault || !polyShader.checkPolyType(PShaderOpenGL.LIGHT)) { if (ppg.defLightShader == null) { String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL); String[] fragSource = pgl.loadFragmentShader(defLightShaderFragURL); - ppg.defLightShader = new PShader(parent, vertSource, fragSource); + ppg.defLightShader = new PShaderOpenGL(parent, vertSource, fragSource); } shader = ppg.defLightShader; } else { @@ -7041,22 +7044,22 @@ protected PShader getPolyShader(boolean lit, boolean tex) { } if (tex) { - if (useDefault || !polyShader.checkPolyType(PShader.TEXTURE)) { + if (useDefault || !polyShader.checkPolyType(PShaderOpenGL.TEXTURE)) { if (ppg.defTextureShader == null) { String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL); String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL); - ppg.defTextureShader = new PShader(parent, vertSource, fragSource); + ppg.defTextureShader = new PShaderOpenGL(parent, vertSource, fragSource); } shader = ppg.defTextureShader; } else { shader = polyShader; } } else { - if (useDefault || !polyShader.checkPolyType(PShader.COLOR)) { + if (useDefault || !polyShader.checkPolyType(PShaderOpenGL.COLOR)) { if (ppg.defColorShader == null) { String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL); String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL); - ppg.defColorShader = new PShader(parent, vertSource, fragSource); + ppg.defColorShader = new PShaderOpenGL(parent, vertSource, fragSource); } shader = ppg.defColorShader; } else { @@ -7073,14 +7076,14 @@ protected PShader getPolyShader(boolean lit, boolean tex) { } - protected PShader getLineShader() { - PShader shader; + protected PShaderOpenGL getLineShader() { + PShaderOpenGL shader; PGraphicsOpenGL ppg = getPrimaryPG(); if (lineShader == null) { if (ppg.defLineShader == null) { String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL); String[] fragSource = pgl.loadFragmentShader(defLineShaderFragURL); - ppg.defLineShader = new PShader(parent, vertSource, fragSource); + ppg.defLineShader = new PShaderOpenGL(parent, vertSource, fragSource); } shader = ppg.defLineShader; } else { @@ -7093,14 +7096,14 @@ protected PShader getLineShader() { } - protected PShader getPointShader() { - PShader shader; + protected PShaderOpenGL getPointShader() { + PShaderOpenGL shader; PGraphicsOpenGL ppg = getPrimaryPG(); if (pointShader == null) { if (ppg.defPointShader == null) { String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL); String[] fragSource = pgl.loadFragmentShader(defPointShaderFragURL); - ppg.defPointShader = new PShader(parent, vertSource, fragSource); + ppg.defPointShader = new PShaderOpenGL(parent, vertSource, fragSource); } shader = ppg.defPointShader; } else { @@ -7276,7 +7279,7 @@ void unbind(PGL pgl) { pgl.disableVertexAttribArray(glLoc); } - boolean active(PShader shader) { + boolean active(PShaderOpenGL shader) { if (active) { if (glLoc == -1) { glLoc = shader.getAttributeLoc(name); diff --git a/core/src/processing/opengl/PShader.java b/core/src/processing/opengl/PShaderOpenGL.java similarity index 97% rename from core/src/processing/opengl/PShader.java rename to core/src/processing/opengl/PShaderOpenGL.java index f3c25e2169..f89874b61c 100644 --- a/core/src/processing/opengl/PShader.java +++ b/core/src/processing/opengl/PShaderOpenGL.java @@ -33,20 +33,16 @@ import java.util.HashMap; /** - * This class encapsulates a GLSL shader program, including a vertex - * and a fragment shader. It is compatible with P2D and P3D, but not - * with the default renderer. + * The OpenGL (GLSL) implementation of {@link processing.core.PShader}. It + * encapsulates a GLSL shader program, including a vertex and a fragment + * shader. It is compatible with P2D and P3D, but not with the default + * renderer. * * Use the loadShader() function to load your shader code. * Note: It's strongly encouraged to use loadShader() to create - * a PShader object, rather than calling the PShader - * constructor manually. - * - * @webref rendering:shaders - * @webBrief This class encapsulates a GLSL shader program, - * including a vertex and a fragment shader + * a PShader object, rather than calling this constructor manually. */ -public class PShader implements PConstants { +public class PShaderOpenGL implements PShader, PConstants { static protected final int POINT = 0; static protected final int LINE = 1; static protected final int POLY = 2; @@ -169,7 +165,7 @@ public class PShader implements PConstants { protected int emissiveLoc; protected int shininessLoc; - public PShader() { + public PShaderOpenGL() { parent = null; pgl = null; context = -1; @@ -192,7 +188,7 @@ public PShader() { } - public PShader(PApplet parent) { + public PShaderOpenGL(PApplet parent) { this(); this.parent = parent; primaryPG = (PGraphicsOpenGL)parent.g; @@ -209,7 +205,7 @@ public PShader(PApplet parent) { * @param vertFilename name of the vertex shader * @param fragFilename name of the fragment shader */ - public PShader(PApplet parent, String vertFilename, String fragFilename) { + public PShaderOpenGL(PApplet parent, String vertFilename, String fragFilename) { this.parent = parent; primaryPG = (PGraphicsOpenGL)parent.g; pgl = primaryPG.pgl; @@ -231,7 +227,7 @@ public PShader(PApplet parent, String vertFilename, String fragFilename) { int vertType = getShaderType(vertexShaderSource, -1); int fragType = getShaderType(fragmentShaderSource, -1); if (vertType == -1 && fragType == -1) { - type = PShader.POLY; + type = PShaderOpenGL.POLY; } else if (vertType == -1) { type = fragType; } else if (fragType == -1) { @@ -248,7 +244,7 @@ public PShader(PApplet parent, String vertFilename, String fragFilename) { * @param vertURL network location of the vertex shader * @param fragURL network location of the fragment shader */ - public PShader(PApplet parent, URL vertURL, URL fragURL) { + public PShaderOpenGL(PApplet parent, URL vertURL, URL fragURL) { this.parent = parent; primaryPG = (PGraphicsOpenGL)parent.g; pgl = primaryPG.pgl; @@ -270,7 +266,7 @@ public PShader(PApplet parent, URL vertURL, URL fragURL) { int vertType = getShaderType(vertexShaderSource, -1); int fragType = getShaderType(fragmentShaderSource, -1); if (vertType == -1 && fragType == -1) { - type = PShader.POLY; + type = PShaderOpenGL.POLY; } else if (vertType == -1) { type = fragType; } else if (fragType == -1) { @@ -282,7 +278,7 @@ public PShader(PApplet parent, URL vertURL, URL fragURL) { } } - public PShader(PApplet parent, String[] vertSource, String[] fragSource) { + public PShaderOpenGL(PApplet parent, String[] vertSource, String[] fragSource) { this.parent = parent; primaryPG = (PGraphicsOpenGL)parent.g; pgl = primaryPG.pgl; @@ -304,7 +300,7 @@ public PShader(PApplet parent, String[] vertSource, String[] fragSource) { int vertType = getShaderType(vertexShaderSource, -1); int fragType = getShaderType(fragmentShaderSource, -1); if (vertType == -1 && fragType == -1) { - type = PShader.POLY; + type = PShaderOpenGL.POLY; } else if (vertType == -1) { type = fragType; } else if (fragType == -1) { @@ -1025,31 +1021,31 @@ static protected int getShaderType(String[] source, int defaultType) { String line = s.trim(); if (PApplet.match(line, colorShaderDefRegexp) != null) - return PShader.COLOR; + return PShaderOpenGL.COLOR; else if (PApplet.match(line, lightShaderDefRegexp) != null) - return PShader.LIGHT; + return PShaderOpenGL.LIGHT; else if (PApplet.match(line, texShaderDefRegexp) != null) - return PShader.TEXTURE; + return PShaderOpenGL.TEXTURE; else if (PApplet.match(line, texlightShaderDefRegexp) != null) - return PShader.TEXLIGHT; + return PShaderOpenGL.TEXLIGHT; else if (PApplet.match(line, polyShaderDefRegexp) != null) - return PShader.POLY; + return PShaderOpenGL.POLY; else if (PApplet.match(line, triShaderAttrRegexp) != null) - return PShader.POLY; + return PShaderOpenGL.POLY; else if (PApplet.match(line, quadShaderAttrRegexp) != null) - return PShader.POLY; + return PShaderOpenGL.POLY; else if (PApplet.match(line, pointShaderDefRegexp) != null) - return PShader.POINT; + return PShaderOpenGL.POINT; else if (PApplet.match(line, lineShaderDefRegexp) != null) - return PShader.LINE; + return PShaderOpenGL.LINE; else if (PApplet.match(line, pointShaderAttrRegexp) != null) - return PShader.POINT; + return PShaderOpenGL.POINT; else if (PApplet.match(line, pointShaderInRegexp) != null) - return PShader.POINT; + return PShaderOpenGL.POINT; else if (PApplet.match(line, lineShaderAttrRegexp) != null) - return PShader.LINE; + return PShaderOpenGL.LINE; else if (PApplet.match(line, lineShaderInRegexp) != null) - return PShader.LINE; + return PShaderOpenGL.LINE; } return defaultType; } @@ -1091,7 +1087,7 @@ protected boolean isPolyShader() { protected boolean checkPolyType(int type) { - if (getType() == PShader.POLY) return true; + if (getType() == PShaderOpenGL.POLY) return true; if (getType() != type) { if (type == TEXLIGHT) { diff --git a/core/src/processing/opengl/PShapeOpenGL.java b/core/src/processing/opengl/PShapeOpenGL.java index f34031f1ac..b97550f13c 100644 --- a/core/src/processing/opengl/PShapeOpenGL.java +++ b/core/src/processing/opengl/PShapeOpenGL.java @@ -5475,7 +5475,7 @@ protected void renderPolys(PGraphicsOpenGL g, PImage textureImage) { Texture tex = textureImage != null ? g.getTexture(textureImage) : null; boolean renderingFill = false, renderingStroke = false; - PShader shader = null; + PShaderOpenGL shader = null; IndexCache cache = tessGeo.polyIndexCache; for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) { if (is3D() || (tex != null && (firstLineIndexCache == -1 || @@ -5658,7 +5658,7 @@ protected void rawPolys(PGraphicsOpenGL g, PImage textureImage) { protected void renderLines(PGraphicsOpenGL g) { - PShader shader = g.getLineShader(); + PShaderOpenGL shader = g.getLineShader(); shader.bind(); IndexCache cache = tessGeo.lineIndexCache; @@ -5755,7 +5755,7 @@ protected void rawLines(PGraphicsOpenGL g) { protected void renderPoints(PGraphicsOpenGL g) { - PShader shader = g.getPointShader(); + PShaderOpenGL shader = g.getPointShader(); shader.bind(); IndexCache cache = tessGeo.pointIndexCache; diff --git a/java/src/processing/mode/java/JavaMode.java b/java/src/processing/mode/java/JavaMode.java index 31d189ff52..26c1963d42 100644 --- a/java/src/processing/mode/java/JavaMode.java +++ b/java/src/processing/mode/java/JavaMode.java @@ -328,7 +328,7 @@ protected String[] getSuggestionIncludeList() { "processing.event.KeyEvent", "processing.event.MouseEvent", "processing.event.TouchEvent", - "processing.opengl.PShader", + "processing.core.PShader", "processing.opengl.PGL", "java.util.ArrayList",