8081491: The case print incomplete

Reviewed-by: alexsch, rchamyal
diff --git a/jdk/src/java.desktop/share/classes/javax/swing/TablePrintable.java b/jdk/src/java.desktop/share/classes/javax/swing/TablePrintable.java
index 6b9053f..b47f869 100644
--- a/jdk/src/java.desktop/share/classes/javax/swing/TablePrintable.java
+++ b/jdk/src/java.desktop/share/classes/javax/swing/TablePrintable.java
@@ -205,11 +205,9 @@
      */
     public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
                                                        throws PrinterException {
-
         // for easy access to these values
         final int imgWidth = (int)pageFormat.getImageableWidth();
         final int imgHeight = (int)pageFormat.getImageableHeight();
-
         if (imgWidth <= 0) {
             throw new PrinterException("Width of printable area is too small.");
         }
@@ -302,10 +300,12 @@
             // been divided by it
             int scaledWidth = (int)(imgWidth / sf);
             int scaledHeight = (int)((availableSpace - hclip.height) / sf);
-
             // calculate the area of the table to be printed for this page
             findNextClip(scaledWidth, scaledHeight);
 
+            if (!((table.getBounds()).intersects(clip))) {
+                return NO_SUCH_PAGE;
+            }
             last++;
         }
 
@@ -343,7 +343,6 @@
         tempRect.width = imgWidth;
         tempRect.height = availableSpace;
         g2d.clip(tempRect);
-
         // if we have a scale factor, scale the graphics object to fit
         // the entire width
         if (sf != 1.0D) {
@@ -389,7 +388,26 @@
 
         // draw a box around the table
         g2d.setColor(Color.BLACK);
-        g2d.drawRect(0, 0, clip.width, hclip.height + clip.height);
+
+        // compute the visible portion of table and draw the rect around it
+        Rectangle visibleBounds = clip.intersection(table.getBounds());
+        Point upperLeft = visibleBounds.getLocation();
+        Point lowerRight = new Point(visibleBounds.x + visibleBounds.width,
+                                     visibleBounds.y + visibleBounds.height);
+
+        int rMin = table.rowAtPoint(upperLeft);
+        int rMax = table.rowAtPoint(lowerRight);
+        if (rMin == -1) {
+            rMin = 0;
+        }
+        if (rMax == -1) {
+            rMax = table.getRowCount();
+        }
+        int rowHeight = 0;
+        for(int visrow = rMin; visrow < rMax; visrow++) {
+            rowHeight += table.getRowHeight(visrow);
+        }
+        g2d.drawRect(0, 0, visibleBounds.width, hclip.height + rowHeight);
 
         // dispose the graphics copy
         g2d.dispose();
@@ -509,7 +527,6 @@
             if (++col >= colCount) {
                 // reset col to 0 to indicate we're finished all columns
                 col = 0;
-
                 break;
             }
 
diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java
index ef0c454..73fd9a5 100644
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java
@@ -1812,9 +1812,11 @@
 
         boolean ltr = table.getComponentOrientation().isLeftToRight();
 
-        Point upperLeft = clip.getLocation();
-        Point lowerRight = new Point(clip.x + clip.width - 1,
-                                     clip.y + clip.height - 1);
+        // compute the visible part of table which needs to be painted
+        Rectangle visibleBounds = clip.intersection(bounds);
+        Point upperLeft = visibleBounds.getLocation();
+        Point lowerRight = new Point(visibleBounds.x + visibleBounds.width - 1,
+                                     visibleBounds.y + visibleBounds.height - 1);
 
         int rMin = table.rowAtPoint(upperLeft);
         int rMax = table.rowAtPoint(lowerRight);
@@ -1843,6 +1845,21 @@
             cMax = table.getColumnCount()-1;
         }
 
+        Container comp = SwingUtilities.getUnwrappedParent(table);
+        if (comp != null) {
+            comp = comp.getParent();
+        }
+
+        if (comp != null && !(comp instanceof JViewport) && !(comp instanceof JScrollPane)) {
+            // We did rMax-1 to paint the same number of rows that are drawn on console
+            // otherwise 1 extra row is printed per page than that are displayed
+            // when there is no scrollPane and we do printing of table
+            // but not when rmax is already pointing to index of last row
+            if (rMax != (table.getRowCount() - 1)) {
+                rMax = rMax - 1;
+            }
+        }
+
         // Paint the grid.
         paintGrid(g, rMin, rMax, cMin, cMax);
 
diff --git a/jdk/test/java/awt/print/PageFormat/ImageableAreaTest.java b/jdk/test/java/awt/print/PageFormat/ImageableAreaTest.java
index f9e7b4b..7bf37415 100644
--- a/jdk/test/java/awt/print/PageFormat/ImageableAreaTest.java
+++ b/jdk/test/java/awt/print/PageFormat/ImageableAreaTest.java
@@ -45,7 +45,7 @@
 
 /**
  * @test
- * @bug 8044444
+ * @bug 8044444 8081491
  * @summary The output's 'Page-n' footer does not show completely
  * @author Alexandr Scherbatiy
  * @run main/manual ImageableAreaTest
@@ -58,11 +58,13 @@
             @Override
             public void run() {
 
+
                 createAndShowTestDialog(
                         "1. Press the Print Table button\n"
                         + " Java print dialog should appear.\n"
                         + "2. Press the Print button on the Java Print dialog.\n"
-                        + "2. Check that the page number is correctly printed.\n"
+                        + "3. Check that the page number is correctly printed.\n"
+                        + "4. Check only the visible part of the table is printed.\n"
                         + "If so, press PASS, else press FAIL.",
                         "Page number is not correctly printed!",
                         ImageableAreaTest::printWithJavaPrintDialog);
@@ -71,24 +73,47 @@
                         "1. Press the Print Table button\n"
                         + " The table should be printed without the print dialog.\n"
                         + "2. Check that the page number is correctly printed.\n"
+                        + "3. Check only the visible part of the table is printed.\n"
                         + "If so, press PASS, else press FAIL.",
                         "Page number is not correctly printed!",
                         ImageableAreaTest::printWithoutPrintDialog);
 
+
+
                 createAndShowTestDialog(
                         "1. Press the Print Table button\n"
                         + " Java print dialog should appear.\n"
                         + "2. Press the Print button on the Java Print dialog.\n"
                         + "3. Check that the table has about half size of the printed page\n"
+                        + "4. Check only the visible part of the table is printed.\n"
                         + "If so, press PASS, else press FAIL.",
                         "Custom imageable area is not correctly printed!",
                         ImageableAreaTest::printWithCustomImageareaSize);
+
+                createAndShowTestDialog(
+                        "1. Press the Print Table button\n"
+                        + " Java print dialog should appear.\n"
+                        + "2. Press the Print button on the Java Print dialog.\n"
+                        + "3. Check that the rows with different height is printed.\n"
+                        + "4. Check only the visible part of the table is printed.\n"
+                        + "If so, press PASS, else press FAIL.",
+                        "Row with different height is not correctly printed!",
+                        ImageableAreaTest::printDifferentRowHeight);
+
+                createAndShowTestDialog(
+                        "1. Press the Print Table button\n"
+                        + " Java print dialog should appear.\n"
+                        + "2. Press the Print button on the Java Print dialog.\n"
+                        + "3. Check that the only 1 row is shown & printed.\n"
+                        + "If so, press PASS, else press FAIL.",
+                        "Only 1 Row is not correctly printed!",
+                        ImageableAreaTest::printOneRowWithJavaPrintDialog);
             }
         });
     }
 
     private static void printWithJavaPrintDialog() {
-        final JTable table = createAuthorTable(42);
+        final JTable table = createAuthorTable(50);
         Printable printable = table.getPrintable(
                 JTable.PrintMode.NORMAL,
                 new MessageFormat("Author Table"),
@@ -110,7 +135,7 @@
 
     private static void printWithoutPrintDialog() {
 
-        final JTable table = createAuthorTable(42);
+        final JTable table = createAuthorTable(50);
         PrintRequestAttributeSet pras
                 = new HashPrintRequestAttributeSet();
         pras.add(new Copies(1));
@@ -132,6 +157,50 @@
         }
     }
 
+    private static void printDifferentRowHeight() {
+        final JTable table = createAuthorTable(50);
+        table.setRowHeight(15, table.getRowHeight(15)+10);
+        Printable printable = table.getPrintable(
+                JTable.PrintMode.NORMAL,
+                new MessageFormat("Author Table"),
+                new MessageFormat("Page - {0}"));
+
+        PrinterJob job = PrinterJob.getPrinterJob();
+        job.setPrintable(printable);
+
+        boolean printAccepted = job.printDialog();
+        if (printAccepted) {
+            try {
+                job.print();
+                closeFrame();
+            } catch (PrinterException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+    }
+
+    private static void printOneRowWithJavaPrintDialog() {
+        final JTable table = createAuthorTable(1);
+        Printable printable = table.getPrintable(
+                JTable.PrintMode.NORMAL,
+                new MessageFormat("Author Table"),
+                new MessageFormat("Page - {0}"));
+
+        PrinterJob job = PrinterJob.getPrinterJob();
+        job.setPrintable(printable);
+
+        boolean printAccepted = job.printDialog();
+        if (printAccepted) {
+            try {
+                job.print();
+                closeFrame();
+            } catch (PrinterException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
     private static void printWithCustomImageareaSize() {
         final JTable table = createAuthorTable(18);
         PrintRequestAttributeSet printAttributes = new HashPrintRequestAttributeSet();
diff --git a/jdk/test/javax/swing/JTable/JTableScrollTest.java b/jdk/test/javax/swing/JTable/JTableScrollTest.java
new file mode 100644
index 0000000..5e0461c
--- /dev/null
+++ b/jdk/test/javax/swing/JTable/JTableScrollTest.java
@@ -0,0 +1,182 @@
+/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Color;
+import java.awt.Dialog;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JFrame;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JButton;
+import javax.swing.table.TableModel;
+import javax.swing.JScrollPane;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.SwingUtilities;
+
+/**
+ * @test
+ * @bug 8081491
+ * @summary Scrolling a JTable creates artifacts
+ * @run main/manual JTableScrollTest
+ */
+public class JTableScrollTest {
+    static JFrame frame = new JFrame();
+    public static void main(String[] args) throws Exception {
+        SwingUtilities.invokeAndWait(new Runnable() {
+
+            @Override
+            public void run() {
+                doTest(JTableScrollTest::createTable);
+            }
+        });
+    }
+
+    private static void createTable() {
+        // final
+        final String[] names = {
+            new String("first_name"),
+            new String("last_name"),
+            new String("favorite_color"),
+            new String("favorite_food")
+        };
+
+        // Create the dummy data (a few rows of names)
+        final Object[][] data = {
+          {"Mike", "Albers",      "green",      "strawberry"},
+          {"Mark", "Andrews",     "blue",       "grapes"},
+          {"Brian", "Beck",       "black",      "raspberry"},
+          {"Lara", "Bunni",       "red",        "strawberry"},
+          {"Roger", "Brinkley",   "blue",       "peach"},
+          {"Brent", "Christian",  "black",      "broccoli"},
+          {"Mark", "Davidson",    "darkgreen",  "asparagus"},
+          {"Jeff", "Dinkins",     "blue",       "kiwi"},
+          {"Ewan", "Dinkins",     "yellow",     "strawberry"},
+          {"Amy", "Fowler",       "violet",     "raspberry"},
+          {"Hania", "Gajewska",   "purple",     "raspberry"},
+          {"David", "Geary",      "blue",       "watermelon"},
+          {"Ryan", "Gosling",    "pink",       "donut"},
+          {"Eric", "Hawkes",      "blue",       "pickle"},
+          {"Shannon", "Hickey",   "green",      "grapes"},
+          {"Earl", "Johnson",     "green",      "carrot"},
+          {"Robi", "Khan",        "green",      "apple"},
+          {"Robert", "Kim",       "blue",       "strawberry"},
+          {"Janet", "Koenig",     "turquoise",  "peach"},
+          {"Jeff", "Kesselman",   "blue",       "pineapple"},
+          {"Onno", "Kluyt",       "orange",     "broccoli"},
+          {"Peter", "Korn",       "sunpurple",  "sparegrass"},
+          {"Rick", "Levenson",    "black",      "raspberry"},
+          {"Brian", "Lichtenwalter", "blue", "pear"},
+          {"Malini", "Minasandram", "beige",    "corn"},
+          {"Michael", "Martak",   "green",      "strawberry"},
+          {"David", "Mendenhall", "forestgreen", "peach"},
+          {"Phil", "Milne",       "pink", "banana"},
+          {"Lynn", "Monsanto",    "cybergreen",  "peach"},
+          {"Hans", "Muller",      "rustred",     "pineapple"},
+          {"Joshua", "Outwater",  "blue",        "pineapple"},
+          {"Tim", "Prinzing",     "blue",        "pepper"},
+          {"Raj", "Premkumar",    "blue",    "broccoli"},
+          {"Howard", "Rosen",     "green",    "strawberry"},
+          {"Ray", "Ryan",         "black",   "banana"},
+          {"Georges", "Saab",     "aqua",     "cantaloupe"},
+          {"Tom", "Santos",       "blue",       "pepper"},
+          {"Rich", "Schiavi",     "blue",       "pepper"},
+          {"Nancy", "Schorr",     "green",      "watermelon"},
+          {"Keith", "Sprochi",    "darkgreen",   "watermelon"},
+          {"Matt", "Tucker",      "eblue",       "broccoli"},
+          {"Dmitri", "Trembovetski", "red",      "tomato"},
+          {"Scott", "Violet",     "violet",      "banana"},
+          {"Kathy", "Walrath",    "darkgreen",   "pear"},
+        };
+
+    // Create a model of the data.
+        TableModel dataModel = new AbstractTableModel() {
+            public int getColumnCount() { return names.length; }
+            public int getRowCount() { return data.length;}
+            public Object getValueAt(int row, int col) {return data[row][col];}
+            public String getColumnName(int column) {return names[column];}
+            public Class getColumnClass(int c) {return getValueAt(0, c).getClass();}
+            public boolean isCellEditable(int row, int col) {return col != 5;}
+            public void setValueAt(Object aValue, int row, int column) { data[row][column] = aValue; }
+         };
+
+    // Create the table
+        JTable tableView = new JTable(dataModel);
+        tableView.setBackground(Color.WHITE);
+        tableView.setForeground(Color.BLACK);
+        tableView.setSize(600, 800);
+        JScrollPane scrollpane = new JScrollPane(tableView);
+        frame.add(scrollpane);
+        frame.pack();
+        frame.setVisible(true);
+    }
+
+    private static void doTest(Runnable action) {
+        String description =
+          "JTable with rows will be displayed along with scrollbar.\n"
+           + "Scroll the table. Verify no arifacts are shown and rows.\n"
+           + " are correctly displayed.";
+        final JDialog dialog = new JDialog();
+        dialog.setTitle("ScrollArtifactTest ");
+        JTextArea textArea = new JTextArea(description);
+        textArea.setEditable(false);
+        final JButton testButton = new JButton("Create Table");
+        final JButton passButton = new JButton("PASS");
+        passButton.setEnabled(false);
+        passButton.addActionListener((e) -> {
+            dialog.dispose();
+            if (frame != null) {
+                frame.setVisible(false);
+                frame.dispose();
+            }
+        });
+        final JButton failButton = new JButton("FAIL");
+        failButton.setEnabled(false);
+        failButton.addActionListener((e) -> {
+            dialog.dispose();
+            if (frame != null) {
+                frame.setVisible(false);
+                frame.dispose();
+            }
+            throw new RuntimeException("Scrollbar artifact shown");
+        });
+        testButton.addActionListener((e) -> {
+            testButton.setEnabled(false);
+            action.run();
+            passButton.setEnabled(true);
+            failButton.setEnabled(true);
+        });
+        JPanel mainPanel = new JPanel(new BorderLayout());
+        mainPanel.add(textArea, BorderLayout.CENTER);
+        JPanel buttonPanel = new JPanel(new FlowLayout());
+        buttonPanel.add(testButton);
+        buttonPanel.add(passButton);
+        buttonPanel.add(failButton);
+        mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+        dialog.add(mainPanel);
+        dialog.pack();
+        dialog.setVisible(true);
+    }
+
+
+}