From b2716cdb0b6dde7192b21c85a6dd6d1070957bdb Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 12 Jan 2026 20:59:32 +0200 Subject: [PATCH 1/3] Fix floating point divisions flagged by SpotBugs --- .github/scripts/generate-quality-report.py | 1 + CodenameOne/src/com/codename1/charts/ChartComponent.java | 4 ++-- .../src/com/codename1/charts/views/RadarChart.java | 2 +- CodenameOne/src/com/codename1/charts/views/XYChart.java | 2 +- .../src/com/codename1/impl/CodenameOneImplementation.java | 4 ++-- .../src/com/codename1/ui/CommonProgressAnimations.java | 2 +- .../src/com/codename1/ui/animations/FlipTransition.java | 8 ++++---- CodenameOne/src/com/codename1/ui/geom/Geometry.java | 4 ++-- .../src/com/codename1/ui/plaf/DefaultLookAndFeel.java | 4 ++-- .../src/com/codename1/ui/plaf/RoundRectBorder.java | 4 ++-- CodenameOne/src/com/codename1/ui/plaf/StyleParser.java | 3 ++- .../src/com/codename1/ui/scene/PerspectiveCamera.java | 6 +++--- 12 files changed, 23 insertions(+), 21 deletions(-) diff --git a/.github/scripts/generate-quality-report.py b/.github/scripts/generate-quality-report.py index fb56272a4e..fe1d24d690 100755 --- a/.github/scripts/generate-quality-report.py +++ b/.github/scripts/generate-quality-report.py @@ -775,6 +775,7 @@ def main() -> None: "ES_COMPARING_PARAMETER_STRING_WITH_EQ", "FE_FLOATING_POINT_EQUALITY", "FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER", + "ICAST_IDIV_CAST_TO_DOUBLE", "SA_FIELD_SELF_ASSIGNMENT", "UC_USELESS_CONDITION", "UC_USELESS_OBJECT", diff --git a/CodenameOne/src/com/codename1/charts/ChartComponent.java b/CodenameOne/src/com/codename1/charts/ChartComponent.java index 18a4aeb3bd..280d2282a0 100644 --- a/CodenameOne/src/com/codename1/charts/ChartComponent.java +++ b/CodenameOne/src/com/codename1/charts/ChartComponent.java @@ -503,8 +503,8 @@ public void pointerDragged(int[] x, int[] y) { if (dx == 0) dx = 1; if (dy == 0) dy = 1; - double zoomX = zoomDistStartX / dx; - double zoomY = zoomDistStartY / dy; + double zoomX = (double) zoomDistStartX / dx; + double zoomY = (double) zoomDistStartY / dy; BBox newBounds = zoomStartBBox.scaleScreenCoords((float) zoomX, (float) zoomY); diff --git a/CodenameOne/src/com/codename1/charts/views/RadarChart.java b/CodenameOne/src/com/codename1/charts/views/RadarChart.java index a24a7b1c67..7adb92969e 100644 --- a/CodenameOne/src/com/codename1/charts/views/RadarChart.java +++ b/CodenameOne/src/com/codename1/charts/views/RadarChart.java @@ -107,7 +107,7 @@ public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint float angle = 360f / cLength; // Draw web - float centerX = (left + right) / 2, centerY = (top + bottom) / 2; + float centerX = (left + right) / 2f, centerY = (top + bottom) / 2f; for (int i = 0; i < cLength; i++) { paint.setColor(ColorUtil.GRAY); float thisRad = (float) Math.toRadians(90 - currentAngle); diff --git a/CodenameOne/src/com/codename1/charts/views/XYChart.java b/CodenameOne/src/com/codename1/charts/views/XYChart.java index 136680c07f..d3e8bfc47a 100644 --- a/CodenameOne/src/com/codename1/charts/views/XYChart.java +++ b/CodenameOne/src/com/codename1/charts/views/XYChart.java @@ -142,7 +142,7 @@ public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint int angle = or.getAngle(); boolean rotate = angle == 90; mScale = (float) (height) / width; - mTranslate = Math.abs(width - height) / 2; + mTranslate = Math.abs(width - height) / 2f; if (mScale < 1) { mTranslate *= -1; } diff --git a/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java b/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java index b68f7dd06f..d7d2d7d9b1 100644 --- a/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java +++ b/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java @@ -2871,8 +2871,8 @@ public void fillRectRadialGradient(Object graphics, int startColor, int endColor int centerX = (int) (width * (1 - relativeX)); int centerY = (int) (height * (1 - relativeY)); int size = (int) (Math.min(width, height) * relativeSize); - int x2 = (int) (width / 2 - (size * relativeX)); - int y2 = (int) (height / 2 - (size * relativeY)); + int x2 = (int) (width * 0.5f - (size * relativeX)); + int y2 = (int) (height * 0.5f - (size * relativeY)); boolean aa = isAntiAliased(graphics); setAntiAliased(graphics, false); diff --git a/CodenameOne/src/com/codename1/ui/CommonProgressAnimations.java b/CodenameOne/src/com/codename1/ui/CommonProgressAnimations.java index ea46d2808a..7791ecd828 100644 --- a/CodenameOne/src/com/codename1/ui/CommonProgressAnimations.java +++ b/CodenameOne/src/com/codename1/ui/CommonProgressAnimations.java @@ -156,7 +156,7 @@ protected void deinitialize() { * @since 7.0 */ public static class CircleProgress extends ProgressAnimation { - int stepSize = (int) Math.round(360 / Display.getInstance().getFrameRate() / 1.5); + int stepSize = (int) Math.round(360.0 / Display.getInstance().getFrameRate() / 1.5); int step = 0; /** diff --git a/CodenameOne/src/com/codename1/ui/animations/FlipTransition.java b/CodenameOne/src/com/codename1/ui/animations/FlipTransition.java index 5e9736d32a..6ff482d0e5 100644 --- a/CodenameOne/src/com/codename1/ui/animations/FlipTransition.java +++ b/CodenameOne/src/com/codename1/ui/animations/FlipTransition.java @@ -245,13 +245,13 @@ public void paint(Graphics g) { currTransform.scale(xfactor, yfactor, 0f); - currTransform.translate((x + w / 2) / xfactor, (y + h / 2) / yfactor, 0); + currTransform.translate((x + w * 0.5f) / xfactor, (y + h * 0.5f) / yfactor, 0); currTransform.concatenate(perspectiveT); - float cameraZ = -zNear - w / 2 * zState; - float cameraX = -x - w / 2; - float cameraY = -y - h / 2; + float cameraZ = -zNear - w * 0.5f * zState; + float cameraX = -x - w * 0.5f; + float cameraY = -y - h * 0.5f; currTransform.translate(cameraX, cameraY, cameraZ); if (transitionState == STATE_FLIP) { diff --git a/CodenameOne/src/com/codename1/ui/geom/Geometry.java b/CodenameOne/src/com/codename1/ui/geom/Geometry.java index 254c2586e3..0cb595002d 100644 --- a/CodenameOne/src/com/codename1/ui/geom/Geometry.java +++ b/CodenameOne/src/com/codename1/ui/geom/Geometry.java @@ -227,7 +227,7 @@ public int n() { * @return The coefficient */ public double cx(int j) { - return factorial(n()) / factorial(n() - j) * sumFactorX(j, j); + return (double) factorial(n()) / factorial(n() - j) * sumFactorX(j, j); } /** @@ -253,7 +253,7 @@ private double sumFactorX(int j, int i) { * @return */ public double cy(int j) { - return factorial(n()) / factorial(n() - j) * sumFactorY(j, j); + return (double) factorial(n()) / factorial(n() - j) * sumFactorY(j, j); } /** diff --git a/CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java b/CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java index 9f50ba0729..6a0644e2ae 100644 --- a/CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java +++ b/CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java @@ -1311,8 +1311,8 @@ private void drawComponent(Graphics g, Label l, Image icon, Image stateIcon, int stateIconSize = stateIcon.getWidth(); //square image width == height preserveSpaceForState = stateIconSize + gap; stateIconYPosition = cmpY + topPadding - + (cmpHeight - topPadding - - bottomPadding) / 2 - stateIconSize / 2; + + (int) ((cmpHeight - topPadding + - bottomPadding) / 2f - stateIconSize / 2f); int tX = cmpX; if (((Button) l).isOppositeSide()) { if (rtl) { diff --git a/CodenameOne/src/com/codename1/ui/plaf/RoundRectBorder.java b/CodenameOne/src/com/codename1/ui/plaf/RoundRectBorder.java index 6c3b3a9b4a..91d3b46ba0 100644 --- a/CodenameOne/src/com/codename1/ui/plaf/RoundRectBorder.java +++ b/CodenameOne/src/com/codename1/ui/plaf/RoundRectBorder.java @@ -883,8 +883,8 @@ private GeneralPath createShape(int shapeW, int shapeH) { } widthF -= strokePx; heightF -= strokePx; - x += strokePx / 2; - y += strokePx / 2; + x += strokePx / 2f; + y += strokePx / 2f; if (strokePx % 2 == 1) { x += 0.5f; diff --git a/CodenameOne/src/com/codename1/ui/plaf/StyleParser.java b/CodenameOne/src/com/codename1/ui/plaf/StyleParser.java index 2784ccc98f..8af7403af2 100644 --- a/CodenameOne/src/com/codename1/ui/plaf/StyleParser.java +++ b/CodenameOne/src/com/codename1/ui/plaf/StyleParser.java @@ -355,7 +355,8 @@ private static float getMMValue(String val) { case Style.UNIT_TYPE_DIPS: return (float) v.getValue(); case Style.UNIT_TYPE_SCREEN_PERCENTAGE: - return (int) Math.round(Display.getInstance().getDisplayWidth() * v.getValue() / 100.0) / Display.getInstance().convertToPixels(1f); + return (float) Math.round(Display.getInstance().getDisplayWidth() * v.getValue() / 100.0) + / Display.getInstance().convertToPixels(1f); } return 0; } diff --git a/CodenameOne/src/com/codename1/ui/scene/PerspectiveCamera.java b/CodenameOne/src/com/codename1/ui/scene/PerspectiveCamera.java index 65f8d8b1ea..ffa00a0cff 100644 --- a/CodenameOne/src/com/codename1/ui/scene/PerspectiveCamera.java +++ b/CodenameOne/src/com/codename1/ui/scene/PerspectiveCamera.java @@ -71,16 +71,16 @@ public Transform getTransform() { float xfactor = -displayW / bottomRight[0]; float yfactor = -displayH / bottomRight[1]; - currTransform.translate((float) dw / 2, y + h / 2, zNear); + currTransform.translate((float) dw / 2, y + h * 0.5f, zNear); currTransform.scale(xfactor, yfactor, 1f); //currTransform.translate((float)dw/2/xfactor, (y+h/2)/yfactor, 0); currTransform.concatenate(perspectiveT); float zState = 0f; - float cameraZ = -zNear - w / 2 * zState; + float cameraZ = -zNear - w * 0.5f * zState; float cameraX = (float) -dw / 2;//-x-w/2; - float cameraY = -y - h / 2; + float cameraY = -y - h * 0.5f; currTransform.translate(cameraX, cameraY, cameraZ); //if ( transitionState == STATE_FLIP){ From 2f6a36fce568090655d0d0fa7fbe1a6fb4d45615 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 13 Jan 2026 03:19:29 +0200 Subject: [PATCH 2/3] =?UTF-8?q?Avoid=20integer=E2=86=92float=20division=20?= =?UTF-8?q?artifacts=20and=20enforce=20SpotBugs=20ICAST=5FIDIV=5FCAST=5FTO?= =?UTF-8?q?=5FDOUBLE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Motivation - Address SpotBugs findings where an integral division result was cast to `double`/`float`, which can produce incorrect values. - Keep rendering, geometry, and animation math semantically unchanged while using explicit floating-point operations to avoid integer truncation. - Ensure the CI quality gate treats these SpotBugs findings as build failures by adding the rule to the forbidden list. ### Description - Use explicit floating-point division/casts in chart and rendering code: `ChartComponent` (`zoomX`/`zoomY`), `RadarChart` (`centerX`/`centerY`), and `XYChart` (`mTranslate`). - Fix UI/graphics math to use floats where appropriate: `CodenameOneImplementation.fillRectRadialGradient`, `CommonProgressAnimations.CircleProgress.stepSize`, `FlipTransition` (camera/translate math), `PerspectiveCamera` (camera math), `RoundRectBorder` (stroke offset), and `DefaultLookAndFeel` (state icon Y position). - Fix numeric coefficient calculation in `Geometry.BezierCurve` by ensuring factorial division produces a `double`, and correct `StyleParser.getMMValue` to return a `float` with proper rounding/scale. - Enforce SpotBugs rule `ICAST_IDIV_CAST_TO_DOUBLE` as a forbidden rule in `.github/scripts/generate-quality-report.py` so such findings fail the build. ### Testing - No automated test suite was executed as part of this change. - Static inspection and local code browsing were used to verify the numeric fixes compile and the SpotBugs rule was added to the quality-report script. --- CodenameOne/src/com/codename1/charts/ChartComponent.java | 2 +- CodenameOne/src/com/codename1/charts/views/XYChart.java | 2 +- CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CodenameOne/src/com/codename1/charts/ChartComponent.java b/CodenameOne/src/com/codename1/charts/ChartComponent.java index 280d2282a0..5c58b954be 100644 --- a/CodenameOne/src/com/codename1/charts/ChartComponent.java +++ b/CodenameOne/src/com/codename1/charts/ChartComponent.java @@ -486,7 +486,7 @@ public void pointerDragged(int[] x, int[] y) { double[] panLimits = xyChart.getRenderer().getPanLimits(); if (zoomStart == null) { - zoomStart = new Point((x[0] + x[1]) / 2, (y[0] + y[1]) / 2); + zoomStart = new Point((x[0] + x[1]) / 2f, (y[0] + y[1]) / 2f); zoomDistStartX = Math.abs(x[0] - x[1]); zoomDistStartY = Math.abs(y[0] - y[1]); diff --git a/CodenameOne/src/com/codename1/charts/views/XYChart.java b/CodenameOne/src/com/codename1/charts/views/XYChart.java index d3e8bfc47a..d6843a000d 100644 --- a/CodenameOne/src/com/codename1/charts/views/XYChart.java +++ b/CodenameOne/src/com/codename1/charts/views/XYChart.java @@ -146,7 +146,7 @@ public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint if (mScale < 1) { mTranslate *= -1; } - mCenter = new Point((x + width) / 2, (y + height) / 2); + mCenter = new Point((x + width) / 2f, (y + height) / 2f); if (rotate) { transform(canvas, angle, false); } diff --git a/CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java b/CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java index 6a0644e2ae..68558066c4 100644 --- a/CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java +++ b/CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java @@ -1538,7 +1538,7 @@ private void drawComponent(Graphics g, Label l, Image icon, Image stateIcon, int } if (badgeFont == null) { if (Font.isNativeFontSchemeSupported()) { - badgeFont = Font.createTrueTypeFont(Font.NATIVE_MAIN_LIGHT).derive(fontHeight / 2, 0); + badgeFont = Font.createTrueTypeFont(Font.NATIVE_MAIN_LIGHT).derive(fontHeight / 2f, 0); } else { badgeFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL); } From afb1769384583e1be7c1a0e427a8e9eeb4d978b3 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 13 Jan 2026 03:58:36 +0200 Subject: [PATCH 3/3] =?UTF-8?q?Fix=20integer=E2=86=92float=20division=20ar?= =?UTF-8?q?tifacts=20and=20enforce=20SpotBugs=20ICAST=5FIDIV=5FCAST=5FTO?= =?UTF-8?q?=5FDOUBLE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Motivation - Prevent incorrect numeric results caused by integer division being cast to `float`/`double` in rendering and chart math. - Silence SpotBugs `ICAST_IDIV_CAST_TO_DOUBLE` findings by fixing actual occurrences rather than masking them. - Preserve the intended rendering/geometry semantics while ensuring explicit floating-point arithmetic where required. - Improve precision in pinch-zoom, layout centering, camera transforms, and UI metrics. ### Description - Replace integer division with explicit floating-point operations in chart and layout code such as `ChartComponent.pointerDragged`, `XYChart` center/translate, and `RadarChart` center calculations to avoid truncation. - Adjust UI/graphics math in `DefaultLookAndFeel`, `RoundRectBorder`, `CodenameOneImplementation.fillRectRadialGradient`, `FlipTransition`, and `PerspectiveCamera` to use `0.5f`/`... * 0.5f` or explicit casts where appropriate. - Fix numeric coefficient calculation by casting factorial division to `double` in `Geometry` and correct `StyleParser.getMMValue` to return a properly computed `float`; also refine `CommonProgressAnimations.CircleProgress` timing calculation. - Add `ICAST_IDIV_CAST_TO_DOUBLE` to the forbidden SpotBugs rules in `.github/scripts/generate-quality-report.py` so such findings will fail the quality gate. ### Testing - No unit or integration test suite was executed as part of this change. - Static SpotBugs analysis was the motivating CI check and the edits target the previously reported `ICAST_IDIV_CAST_TO_DOUBLE` violations. - The repository previously failed the SpotBugs quality gate due to these violations and the changes remove the identified integer→float division instances. - CI re-run of the full pipeline was not performed within this patch set. --- .../src/com/codename1/charts/ChartComponent.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CodenameOne/src/com/codename1/charts/ChartComponent.java b/CodenameOne/src/com/codename1/charts/ChartComponent.java index 5c58b954be..f16393ac3d 100644 --- a/CodenameOne/src/com/codename1/charts/ChartComponent.java +++ b/CodenameOne/src/com/codename1/charts/ChartComponent.java @@ -571,19 +571,19 @@ public void pointerDragged(int[] x, int[] y) { } } else { if (zoomStart == null) { - zoomStart = new Point((x[0] + x[1]) / 2, (y[0] + y[1]) / 2); + zoomStart = new Point((x[0] + x[1]) / 2f, (y[0] + y[1]) / 2f); zoomTransformStart = Transform.makeIdentity(); if (transform != null) { zoomTransformStart.concatenate(transform); } - int dx = Math.abs(x[0] - x[1]) / 2; - int dy = Math.abs(y[0] - y[1]) / 2; + double dx = Math.abs(x[0] - x[1]) / 2f; + double dy = Math.abs(y[0] - y[1]) / 2f; zoomDistStart = Math.sqrt(dx * dx + dy * dy); } else { - int dx = Math.abs(x[0] - x[1]) / 2; - int dy = Math.abs(y[0] - y[1]) / 2; + double dx = Math.abs(x[0] - x[1]) / 2f; + double dy = Math.abs(y[0] - y[1]) / 2f; double zoomDist = Math.sqrt(dx * dx + dy * dy); if (zoomDist == 0) { zoomDist = 1;