From 111aceb63e6c38e17e89cf32939bf9f2ab567143 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 24 Jun 2015 13:39:32 -0700 Subject: [PATCH 001/199] 8140802: Clean up and refactor of class loading code for CDS Reviewed-by: mchung, jiangli --- jdk/make/src/classes/build/tools/module/boot.modules | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/make/src/classes/build/tools/module/boot.modules b/jdk/make/src/classes/build/tools/module/boot.modules index 02de8910651..296d393eea8 100644 --- a/jdk/make/src/classes/build/tools/module/boot.modules +++ b/jdk/make/src/classes/build/tools/module/boot.modules @@ -19,6 +19,7 @@ java.xml.crypto jdk.charsets jdk.deploy jdk.deploy.osx +jdk.vm.cds jdk.httpserver jdk.jfr jdk.management From 325d83e6893f70c1b04f19166664dfab185673f9 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 22 Jul 2015 20:14:16 -0700 Subject: [PATCH 002/199] 8140802: Clean up and refactor of class loading code for CDS Reviewed-by: jiangli, acorn --- modules.xml | 1 + test/lib/sun/hotspot/WhiteBox.java | 1 + 2 files changed, 2 insertions(+) diff --git a/modules.xml b/modules.xml index 94b55f7c691..1d5d8bfbc99 100644 --- a/modules.xml +++ b/modules.xml @@ -340,6 +340,7 @@ java.sql java.sql.rowset jdk.scripting.nashorn + jdk.vm.cds sun.reflect.annotation diff --git a/test/lib/sun/hotspot/WhiteBox.java b/test/lib/sun/hotspot/WhiteBox.java index 2af9e12d0f3..93c24705b7e 100644 --- a/test/lib/sun/hotspot/WhiteBox.java +++ b/test/lib/sun/hotspot/WhiteBox.java @@ -407,6 +407,7 @@ public class WhiteBox { public native void assertMatchingSafepointCalls(boolean mutexSafepointValue, boolean attemptedNoSafepointValue); // Sharing + public native boolean isSharedClass(Class c); public native boolean isShared(Object o); public native boolean areSharedStringsIgnored(); } From 138d45b4769669eeb5a6d39f7312ffea890df205 Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan Date: Mon, 28 Sep 2015 08:42:06 -0700 Subject: [PATCH 003/199] 8066272: pack200 must support Multi-Release Jars Reviewed-by: jrose, sdrach --- .../sun/java/util/jar/pack/PackerImpl.java | 47 ++-- jdk/test/tools/pack200/MultiRelease.java | 257 ++++++++++++++++++ 2 files changed, 284 insertions(+), 20 deletions(-) create mode 100644 jdk/test/tools/pack200/MultiRelease.java diff --git a/jdk/src/java.base/share/classes/com/sun/java/util/jar/pack/PackerImpl.java b/jdk/src/java.base/share/classes/com/sun/java/util/jar/pack/PackerImpl.java index 5e7da4bb6d4..203fde39335 100644 --- a/jdk/src/java.base/share/classes/com/sun/java/util/jar/pack/PackerImpl.java +++ b/jdk/src/java.base/share/classes/com/sun/java/util/jar/pack/PackerImpl.java @@ -272,22 +272,6 @@ public class PackerImpl extends TLGlobals implements Pack200.Packer { // (Done collecting options from props.) - boolean isClassFile(String name) { - if (!name.endsWith(".class")) return false; - for (String prefix = name; ; ) { - if (passFiles.contains(prefix)) return false; - int chop = prefix.lastIndexOf('/'); - if (chop < 0) break; - prefix = prefix.substring(0, chop); - } - return true; - } - - boolean isMetaInfFile(String name) { - return name.startsWith("/" + Utils.METAINF) || - name.startsWith(Utils.METAINF); - } - // Get a new package, based on the old one. private void makeNextPackage() { pkg.reset(); @@ -332,6 +316,29 @@ public class PackerImpl extends TLGlobals implements Pack200.Packer { InFile(JarEntry je) { this(null, je); } + boolean isClassFile() { + if (!name.endsWith(".class")) { + return false; + } + for (String prefix = name;;) { + if (passFiles.contains(prefix)) { + return false; + } + int chop = prefix.lastIndexOf('/'); + if (chop < 0) { + break; + } + prefix = prefix.substring(0, chop); + } + return true; + } + boolean isMetaInfFile() { + return name.startsWith("/" + Utils.METAINF) + || name.startsWith(Utils.METAINF); + } + boolean mustProcess() { + return !isMetaInfFile() && isClassFile(); + } long getInputLength() { long len = (je != null)? je.getSize(): f.length(); assert(len >= 0) : this+".len="+len; @@ -391,7 +398,7 @@ public class PackerImpl extends TLGlobals implements Pack200.Packer { Package.File file = null; // (5078608) : discount the resource files in META-INF // from segment computation. - long inflen = (isMetaInfFile(name)) + long inflen = (inFile.isMetaInfFile()) ? 0L : inFile.getInputLength(); @@ -406,7 +413,7 @@ public class PackerImpl extends TLGlobals implements Pack200.Packer { assert(je.isDirectory() == name.endsWith("/")); - if (isClassFile(name)) { + if (inFile.mustProcess()) { file = readClass(name, bits.getInputStream()); } if (file == null) { @@ -429,7 +436,7 @@ public class PackerImpl extends TLGlobals implements Pack200.Packer { for (InFile inFile : inFiles) { String name = inFile.name; // (5078608) : discount the resource files completely from segmenting - long inflen = (isMetaInfFile(name)) + long inflen = (inFile.isMetaInfFile()) ? 0L : inFile.getInputLength() ; if ((segmentSize += inflen) > segmentLimit) { @@ -447,7 +454,7 @@ public class PackerImpl extends TLGlobals implements Pack200.Packer { if (verbose > 1) Utils.log.fine("Reading " + name); Package.File file = null; - if (isClassFile(name)) { + if (inFile.mustProcess()) { file = readClass(name, strm); if (file == null) { strm.close(); diff --git a/jdk/test/tools/pack200/MultiRelease.java b/jdk/test/tools/pack200/MultiRelease.java new file mode 100644 index 00000000000..7b3f8a3b91a --- /dev/null +++ b/jdk/test/tools/pack200/MultiRelease.java @@ -0,0 +1,257 @@ +/* + * 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). + * + r 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.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @bug 8066272 + * @summary tests a simple multi-versioned jar file + * @compile -XDignore.symbol.file Utils.java MultiRelease.java + * @run main MultiRelease + * @author ksrini + */ + +public class MultiRelease { + private static final File cwd = new File("."); + private static int pass = 0; + private static int fail = 0; + // specify alternate name via arguments to verify + // if permanent fix works + + private static final String PropKey = "pack200.MultiRelease.META-INF"; + private static final String MetaInfName = System.getProperty(PropKey, "META-INF"); + + public static void main(String... args) throws Exception { + new MultiRelease().run(); + } + + void run() throws Exception { + List testCases = new ArrayList<>(); + testCases.add(new TestCase1()); + testCases.add(new TestCase2()); + for (TestCase tc : testCases) { + tc.run(); + } + if (fail > 0) { + throw new Exception(fail + "/" + testCases.size() + " tests fails"); + } else { + System.out.println("All tests(" + pass + ") passes"); + } + } + + /* + * An abstract class to eliminate test boiler plating. + */ + static abstract class TestCase { + final File tcwd; + final File metaInfDir; + final File versionsDir; + final File manifestFile; + + TestCase(String directory) throws IOException { + System.out.println("initializing directories"); + tcwd = new File(cwd, directory); + metaInfDir = mkdir(new File(tcwd, MetaInfName)); + versionsDir = mkdir(new File(metaInfDir, "versions")); + manifestFile = new File(tcwd, "manifest.tmp"); + List scratch = new ArrayList<>(); + scratch.add("Multi-Release: true"); + Utils.createFile(manifestFile, scratch); + } + + File mkdir(File f) throws IOException { + if (f.exists() && f.isDirectory() && f.canRead() && f.canWrite()) { + return f; + } + if (!f.mkdirs()) { + throw new IOException("mkdirs failed: " + f.getAbsolutePath()); + } + return f; + } + + abstract void emitClassFiles() throws Exception; + + void run() { + try { + emitClassFiles(); + // jar the file up + File testFile = new File(tcwd, "test" + Utils.JAR_FILE_EXT); + Utils.jar("cvfm", + testFile.getAbsolutePath(), + manifestFile.getAbsolutePath(), + "-C", + tcwd.getAbsolutePath(), + "."); + File outFile = new File(tcwd, "test-repacked" + Utils.JAR_FILE_EXT); + List cmdsList = new ArrayList<>(); + + cmdsList.add(Utils.getPack200Cmd()); + cmdsList.add("-J-ea"); + cmdsList.add("-J-esa"); + cmdsList.add("-v"); + cmdsList.add("--repack"); + cmdsList.add(outFile.getAbsolutePath()); + cmdsList.add(testFile.getAbsolutePath()); + List output = Utils.runExec(cmdsList); + Utils.doCompareVerify(testFile.getAbsoluteFile(), outFile.getAbsoluteFile()); + pass++; + } catch (Exception e) { + e.printStackTrace(System.err); + fail++; + } + } + } + + static class TestCase1 extends TestCase { + private TestCase1(String directory) throws IOException { + super(directory); + } + + public TestCase1() throws Exception { + this("case1"); + } + + @Override + void emitClassFiles() throws Exception { + emitClassFile(""); + emitClassFile("7"); + emitClassFile("8"); + emitClassFile("9"); + } + + /* + * Adds different variants of types + */ + void emitClassFile(String version) throws IOException { + final File outDir = mkdir(version.isEmpty() + ? tcwd + : new File(versionsDir, version)); + + final File srcDir = mkdir(version.isEmpty() + ? new File(tcwd, "src") + : new File(new File(versionsDir, version), "src")); + + final String fname = "Foo"; + final File srcFile = new File(srcDir, fname + Utils.JAVA_FILE_EXT); + List scratch = new ArrayList<>(); + + scratch.add("package pkg;"); + switch (version) { + case "7": + scratch.add("public class Foo {"); + scratch.add("public static final class Bar {}"); + break; + case "8": + scratch.add("public abstract class Foo {"); + scratch.add("public final class Bar {}"); + break; + case "9": + scratch.add("public interface Foo {"); + scratch.add("public final class Bar {}"); + break; + default: + scratch.add("public class Foo {"); + scratch.add("public final class Bar {}"); + break; + } + scratch.add("}"); + + Utils.createFile(srcFile, scratch); + Utils.compiler("-d", + outDir.getAbsolutePath(), + srcFile.getAbsolutePath()); + } + } + + static class TestCase2 extends TestCase { + private TestCase2(String directory) throws IOException { + super(directory); + } + + TestCase2() throws Exception { + this("case2"); + } + + @Override + void emitClassFiles() throws Exception { + emitClassFile(""); + emitClassFile("8"); + } + + /* + * Adds different variants of types and tries to invoke an + * interface or concrete method defined by them. + */ + void emitClassFile(String version) throws IOException { + + final File outDir = mkdir(version.isEmpty() + ? tcwd + : new File(versionsDir, version)); + + final File srcDir = mkdir(version.isEmpty() + ? new File(tcwd, "src") + : new File(new File(versionsDir, version), "src")); + + List scratch = new ArrayList<>(); + final String fname1 = "Ab"; + final File srcFile1 = new File(srcDir, fname1 + Utils.JAVA_FILE_EXT); + + final String fname2 = "AbNormal"; + final File srcFile2 = new File(srcDir, fname2 + Utils.JAVA_FILE_EXT); + switch (version) { + case "8": + scratch.clear(); + scratch.add("import java.io.IOException;"); + scratch.add("public interface " + fname1 + "{"); + scratch.add(" public abstract void close() throws IOException ;"); + scratch.add("}"); + Utils.createFile(srcFile1, scratch); + break; + default: + scratch.clear(); + scratch.add("import java.io.IOException;"); + scratch.add("public abstract class " + fname1 + "{"); + scratch.add(" public abstract void close() throws IOException ;"); + scratch.add("}"); + Utils.createFile(srcFile1, scratch); + } + + scratch.clear(); + scratch.add("import java.io.IOException;"); + scratch.add("public class " + fname2 + "{"); + scratch.add(" public void doSomething(Ab ab) throws IOException {"); + scratch.add(" ab.close();"); + scratch.add(" }"); + scratch.add("}"); + + Utils.createFile(srcFile2, scratch); + Utils.compiler("-d", + outDir.getAbsolutePath(), + srcFile1.getAbsolutePath(), + srcFile2.getAbsolutePath()); + } + } +} From 7b6f3bbed85892d3078dc69717e6a5acf00d71d0 Mon Sep 17 00:00:00 2001 From: Peter Brunet Date: Thu, 12 Nov 2015 12:27:36 -0600 Subject: [PATCH 004/199] 8134116: Add more comprehensive fix and regression test for JDK-8133897 Use getTitleAt instead of Page.title field; add regression test Reviewed-by: alexsch, serb --- .../classes/javax/swing/JTabbedPane.java | 27 ++-- .../swing/JTabbedPane/8134116/Bug8134116.java | 130 ++++++++++++++++++ 2 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 jdk/test/javax/swing/JTabbedPane/8134116/Bug8134116.java diff --git a/jdk/src/java.desktop/share/classes/javax/swing/JTabbedPane.java b/jdk/src/java.desktop/share/classes/javax/swing/JTabbedPane.java index d50e660e413..de917a34e00 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/JTabbedPane.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/JTabbedPane.java @@ -2095,9 +2095,10 @@ public class JTabbedPane extends JComponent */ void setDisplayedMnemonicIndex(int mnemonicIndex) { if (this.mnemonicIndex != mnemonicIndex) { - if (mnemonicIndex != -1 && (title == null || + String t = getTitle(); + if (mnemonicIndex != -1 && (t == null || mnemonicIndex < 0 || - mnemonicIndex >= title.length())) { + mnemonicIndex >= t.length())) { throw new IllegalArgumentException( "Invalid mnemonic index: " + mnemonicIndex); } @@ -2116,7 +2117,7 @@ public class JTabbedPane extends JComponent void updateDisplayedMnemonicIndex() { setDisplayedMnemonicIndex( - SwingUtilities.findDisplayedMnemonicIndex(title, mnemonic)); + SwingUtilities.findDisplayedMnemonicIndex(getTitle(), mnemonic)); } ///////////////// @@ -2133,10 +2134,9 @@ public class JTabbedPane extends JComponent public String getAccessibleName() { if (accessibleName != null) { return accessibleName; - } else if (title != null) { - return title; + } else { + return getTitle(); } - return null; } public String getAccessibleDescription() { @@ -2156,7 +2156,7 @@ public class JTabbedPane extends JComponent AccessibleStateSet states; states = parent.getAccessibleContext().getAccessibleStateSet(); states.add(AccessibleState.SELECTABLE); - int i = parent.indexOfTab(title); + int i = parent.indexOfTabComponent(tabComponent); if (i == parent.getSelectedIndex()) { states.add(AccessibleState.SELECTED); } @@ -2164,7 +2164,7 @@ public class JTabbedPane extends JComponent } public int getAccessibleIndexInParent() { - return parent.indexOfTab(title); + return parent.indexOfTabComponent(tabComponent); } public int getAccessibleChildrenCount() { @@ -2272,10 +2272,8 @@ public class JTabbedPane extends JComponent } public Rectangle getBounds() { - int i = parent.indexOfTab(title); - // Check for no title. Even though that's a bug in the app we should - // inhibit an ArrayIndexOutOfBoundsException from getTabBounds. - return (i == -1) ? null : parent.getUI().getTabBounds(parent, i); + return parent.getUI(). + getTabBounds(parent, parent.indexOfTabComponent(tabComponent)); } public void setBounds(Rectangle r) { @@ -2343,6 +2341,11 @@ public class JTabbedPane extends JComponent return null; } } + + private String getTitle() { + return getTitleAt(parent.indexOfComponent(component)); + } + } /** diff --git a/jdk/test/javax/swing/JTabbedPane/8134116/Bug8134116.java b/jdk/test/javax/swing/JTabbedPane/8134116/Bug8134116.java new file mode 100644 index 00000000000..15aa9f43260 --- /dev/null +++ b/jdk/test/javax/swing/JTabbedPane/8134116/Bug8134116.java @@ -0,0 +1,130 @@ + +import java.awt.*; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.List; +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleComponent; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.swing.*; +import javax.swing.plaf.nimbus.NimbusLookAndFeel; + +/* + * @test + * @bug 8134116 + * @summary JTabbedPane$Page.getBounds throws IndexOutOfBoundsException + * @run main Bug8134116 + */ +public class Bug8134116 { + + public static void main(String args[]) throws Exception { + + try { + UIManager.setLookAndFeel(new NimbusLookAndFeel()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + SwingUtilities.invokeAndWait(() -> { + JPanel panel0 = new JPanel(); + BadPane badPane = new BadPane(); + badPane.add("zero", panel0); + badPane.add("one", null); + JFrame frame = new JFrame(); + frame.add(badPane); + frame.setSize(300, 300); + frame.setVisible(true); + + AccessibleContext ac = badPane.getAccessibleContext(); + Accessible page0 = ac.getAccessibleChild(0); + if (page0 == null) { + // Not something being tested, but checking anyway + throw new RuntimeException("getAccessibleChild(0) is null"); + } + Accessible page1 = ac.getAccessibleChild(1); + if (page1 == null) { + // Not something being tested, but checking anyway + throw new RuntimeException("getAccessibleChild(1) is null"); + } + // page0 and page1 are a JTabbedPane.Page, a private inner class + // and is an AccessibleContext + // and implements Accessible and AccessibleComponent + AccessibleContext pac0 = page0.getAccessibleContext(); + AccessibleContext pac1 = page1.getAccessibleContext(); + + // the following would fail if JDK-8134116 fix not present + + // test Page.getBounds + // ensure no IndexOutOfBoundsException + pac0.getAccessibleComponent().getBounds(); + + // test Page.getAccessibleStateSet + // At this point page 0 is selected + AccessibleStateSet accSS0 = pac0.getAccessibleStateSet(); + if (!accSS0.contains(AccessibleState.SELECTED)) { + String msg = "Empty title -> AccessibleState.SELECTED not set"; + throw new RuntimeException(msg); + } + + // test Page.getAccessibleIndexInParent + if (pac0.getAccessibleIndexInParent() == -1) { + String msg = "Empty title -> negative AccessibleIndexInParent"; + throw new RuntimeException(msg); + } + + // test Page.getAccessibleName + String accName = pac0.getAccessibleName(); + if (!accName.equals("zero")) { + String msg = "Empty title -> empty AccessibleName"; + throw new RuntimeException(msg); + } + // test Page.getAccessibleName when component is null + accName = pac1.getAccessibleName(); + if (!accName.equals("one")) { + String msg = "AccessibleName of null panel not 'one'"; + throw new RuntimeException(msg); + } + + // test Page.setDisplayedMnemonicIndex + // Empty title -> IllegalArgumnetException + badPane.setDisplayedMnemonicIndexAt(0, 1); + + // test Page.updateDisplayedMnemonicIndex + badPane.setMnemonicAt(0, KeyEvent.VK_Z); + if (badPane.getDisplayedMnemonicIndexAt(0) == -1) { + String msg="Empty title -> getDisplayedMnemonicIndexAt failure"; + throw new RuntimeException(msg); + } + }); + } + + // The following is likely what is being done in Burp Suite + // https://portswigger.net/burp/ which fails in the same way, i.e. the + // pages List in JTabbedPane is not being managed properly and thus + // Page.title is "" for each page. The overridden insertTab manages titles + // in the subclass passing a "" title to the superclass JTabbedPane through + // its insertTab. Later an overridden getTitleAt returns the titles as + // managed by the subclass. + static class BadPane extends JTabbedPane { + private List titles; + + BadPane() { + titles = new ArrayList(1); + } + + @Override + public void insertTab( String title, Icon icon, Component component, + String tip, int index ) { + titles.add(index, title); + super.insertTab("", icon, component, tip, index); + } + + @Override + public String getTitleAt(int i) { + return titles.get(i); + } + } + +} From a00e011325156ae93660cb2ebbaba543fa545c33 Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Fri, 13 Nov 2015 05:02:26 -0800 Subject: [PATCH 005/199] 8073320: Windows HiDPI Graphics support Reviewed-by: flar, serb --- .../windows/native/launcher/java.manifest | 2 +- .../sun/java2d/opengl/CGLSurfaceData.java | 7 +- .../classes/sun/lwawt/LWWindowPeer.java | 4 +- .../sun/awt/image/BufImgSurfaceData.java | 101 +++++-- .../image/BufferedImageGraphicsConfig.java | 31 ++- .../sun/awt/image/SunVolatileImage.java | 13 +- .../classes/sun/awt/image/SurfaceManager.java | 24 +- .../sun/awt/image/VolatileSurfaceManager.java | 12 +- .../classes/sun/java2d/SunGraphics2D.java | 183 ++++++++----- .../sun/java2d/SunGraphicsEnvironment.java | 48 ++++ .../share/classes/sun/java2d/SurfaceData.java | 16 +- .../classes/sun/java2d/pipe/DrawImage.java | 7 +- .../classes/sun/awt/Win32GraphicsConfig.java | 6 +- .../classes/sun/awt/Win32GraphicsDevice.java | 40 +++ .../sun/awt/Win32GraphicsEnvironment.java | 18 ++ .../classes/sun/awt/windows/WWindowPeer.java | 9 +- .../sun/java2d/d3d/D3DSurfaceData.java | 38 ++- .../sun/java2d/opengl/WGLSurfaceData.java | 27 +- .../java2d/windows/GDIWindowSurfaceData.java | 22 +- .../native/libawt/windows/MouseInfo.cpp | 13 +- .../native/libawt/windows/awt_Choice.cpp | 9 +- .../native/libawt/windows/awt_Component.cpp | 63 ++++- .../native/libawt/windows/awt_Component.h | 5 + .../native/libawt/windows/awt_Font.cpp | 58 +++- .../native/libawt/windows/awt_Robot.cpp | 24 +- .../native/libawt/windows/awt_Toolkit.cpp | 14 +- .../windows/awt_Win32GraphicsConfig.cpp | 24 +- .../windows/awt_Win32GraphicsDevice.cpp | 168 ++++++++++++ .../libawt/windows/awt_Win32GraphicsDevice.h | 10 + .../native/libawt/windows/awt_Window.cpp | 93 +++++-- .../native/libawt/windows/awt_Window.h | 1 + .../HiDPIMouseClick/HiDPIRobotMouseClick.java | 87 ++++++ .../HiDPIRobotScreenCaptureTest.java | 115 ++++++++ .../HiDPIPropertiesWindowsTest.java | 139 ++++++++++ ...iResolutionDrawImageWithTransformTest.java | 248 ++++++++++++++++++ 35 files changed, 1498 insertions(+), 181 deletions(-) create mode 100644 jdk/test/java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java create mode 100644 jdk/test/java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java create mode 100644 jdk/test/java/awt/hidpi/properties/HiDPIPropertiesWindowsTest.java create mode 100644 jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java diff --git a/jdk/src/java.base/windows/native/launcher/java.manifest b/jdk/src/java.base/windows/native/launcher/java.manifest index eee6045f3f8..882d93c9a68 100644 --- a/jdk/src/java.base/windows/native/launcher/java.manifest +++ b/jdk/src/java.base/windows/native/launcher/java.manifest @@ -37,7 +37,7 @@ - true + true/PM diff --git a/jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java b/jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java index c260e60ad68..b59349f9cb8 100644 --- a/jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java +++ b/jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java @@ -166,7 +166,12 @@ public abstract class CGLSurfaceData extends OGLSurfaceData { } @Override - public int getDefaultScale() { + public double getDefaultScaleX() { + return scale; + } + + @Override + public double getDefaultScaleY() { return scale; } diff --git a/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java b/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java index 676726863f8..fabc2a21ba1 100644 --- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java +++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java @@ -1156,7 +1156,9 @@ public class LWWindowPeer && !(dst instanceof NullSurfaceData) && !(src instanceof NullSurfaceData) && src.getSurfaceType().equals(dst.getSurfaceType()) - && src.getDefaultScale() == dst.getDefaultScale()) { + && src.getDefaultScaleX() == dst.getDefaultScaleX() + && src.getDefaultScaleY() == dst.getDefaultScaleY()) + { final Rectangle size = src.getBounds(); final Blit blit = Blit.locate(src.getSurfaceType(), CompositeType.Src, diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java b/jdk/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java index ac26802b027..8d37a2d6a1d 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java @@ -50,6 +50,8 @@ public class BufImgSurfaceData extends SurfaceData { BufferedImage bufImg; private BufferedImageGraphicsConfig graphicsConfig; RenderLoops solidloops; + private final double scaleX; + private final double scaleY; private static native void initIDs(Class ICM, Class ICMColorData); @@ -73,6 +75,12 @@ public class BufImgSurfaceData extends SurfaceData { } public static SurfaceData createData(BufferedImage bufImg) { + return createData(bufImg, 1, 1); + } + + public static SurfaceData createData(BufferedImage bufImg, + double scaleX, double scaleY) + { if (bufImg == null) { throw new NullPointerException("BufferedImage cannot be null"); } @@ -82,31 +90,36 @@ public class BufImgSurfaceData extends SurfaceData { // REMIND: Check the image type and pick an appropriate subclass switch (type) { case BufferedImage.TYPE_INT_BGR: - sData = createDataIC(bufImg, SurfaceType.IntBgr); + sData = createDataIC(bufImg, SurfaceType.IntBgr, scaleX, scaleY); break; case BufferedImage.TYPE_INT_RGB: - sData = createDataIC(bufImg, SurfaceType.IntRgb); + sData = createDataIC(bufImg, SurfaceType.IntRgb, scaleX, scaleY); break; case BufferedImage.TYPE_INT_ARGB: - sData = createDataIC(bufImg, SurfaceType.IntArgb); + sData = createDataIC(bufImg, SurfaceType.IntArgb, scaleX, scaleY); break; case BufferedImage.TYPE_INT_ARGB_PRE: - sData = createDataIC(bufImg, SurfaceType.IntArgbPre); + sData = createDataIC(bufImg, SurfaceType.IntArgbPre, scaleX, scaleY); break; case BufferedImage.TYPE_3BYTE_BGR: - sData = createDataBC(bufImg, SurfaceType.ThreeByteBgr, 2); + sData = createDataBC(bufImg, SurfaceType.ThreeByteBgr, 2, + scaleX, scaleY); break; case BufferedImage.TYPE_4BYTE_ABGR: - sData = createDataBC(bufImg, SurfaceType.FourByteAbgr, 3); + sData = createDataBC(bufImg, SurfaceType.FourByteAbgr, 3, + scaleX, scaleY); break; case BufferedImage.TYPE_4BYTE_ABGR_PRE: - sData = createDataBC(bufImg, SurfaceType.FourByteAbgrPre, 3); + sData = createDataBC(bufImg, SurfaceType.FourByteAbgrPre, 3, + scaleX, scaleY); break; case BufferedImage.TYPE_USHORT_565_RGB: - sData = createDataSC(bufImg, SurfaceType.Ushort565Rgb, null); + sData = createDataSC(bufImg, SurfaceType.Ushort565Rgb, null, + scaleX, scaleY); break; case BufferedImage.TYPE_USHORT_555_RGB: - sData = createDataSC(bufImg, SurfaceType.Ushort555Rgb, null); + sData = createDataSC(bufImg, SurfaceType.Ushort555Rgb, null, + scaleX, scaleY); break; case BufferedImage.TYPE_BYTE_INDEXED: { @@ -128,14 +141,16 @@ public class BufImgSurfaceData extends SurfaceData { default: throw new InternalError("Unrecognized transparency"); } - sData = createDataBC(bufImg, sType, 0); + sData = createDataBC(bufImg, sType, 0, scaleX, scaleY); } break; case BufferedImage.TYPE_BYTE_GRAY: - sData = createDataBC(bufImg, SurfaceType.ByteGray, 0); + sData = createDataBC(bufImg, SurfaceType.ByteGray, 0, + scaleX, scaleY); break; case BufferedImage.TYPE_USHORT_GRAY: - sData = createDataSC(bufImg, SurfaceType.UshortGray, null); + sData = createDataSC(bufImg, SurfaceType.UshortGray, null, + scaleX, scaleY); break; case BufferedImage.TYPE_BYTE_BINARY: { @@ -154,7 +169,7 @@ public class BufImgSurfaceData extends SurfaceData { default: throw new InternalError("Unrecognized pixel size"); } - sData = createDataBP(bufImg, sType); + sData = createDataBP(bufImg, sType, scaleX, scaleY); } break; case BufferedImage.TYPE_CUSTOM: @@ -191,7 +206,7 @@ public class BufImgSurfaceData extends SurfaceData { sType = SurfaceType.AnyDcm; } } - sData = createDataIC(bufImg, sType); + sData = createDataIC(bufImg, sType, scaleX, scaleY); break; } else if (raster instanceof ShortComponentRaster && raster.getNumDataElements() == 1 && @@ -233,11 +248,12 @@ public class BufImgSurfaceData extends SurfaceData { icm = null; } } - sData = createDataSC(bufImg, sType, icm); + sData = createDataSC(bufImg, sType, icm, scaleX, scaleY); break; } - sData = new BufImgSurfaceData(raster.getDataBuffer(), - bufImg, SurfaceType.Custom); + sData = new BufImgSurfaceData(raster.getDataBuffer(), bufImg, + SurfaceType.Custom, + scaleX, scaleY); } break; } @@ -250,11 +266,15 @@ public class BufImgSurfaceData extends SurfaceData { } public static SurfaceData createDataIC(BufferedImage bImg, - SurfaceType sType) { + SurfaceType sType, + double scaleX, + double scaleY) + { IntegerComponentRaster icRaster = (IntegerComponentRaster)bImg.getRaster(); BufImgSurfaceData bisd = - new BufImgSurfaceData(icRaster.getDataBuffer(), bImg, sType); + new BufImgSurfaceData(icRaster.getDataBuffer(), bImg, sType, + scaleX, scaleY); bisd.initRaster(icRaster.getDataStorage(), icRaster.getDataOffset(0) * 4, 0, icRaster.getWidth(), @@ -267,11 +287,14 @@ public class BufImgSurfaceData extends SurfaceData { public static SurfaceData createDataSC(BufferedImage bImg, SurfaceType sType, - IndexColorModel icm) { + IndexColorModel icm, + double scaleX, double scaleY) + { ShortComponentRaster scRaster = (ShortComponentRaster)bImg.getRaster(); BufImgSurfaceData bisd = - new BufImgSurfaceData(scRaster.getDataBuffer(), bImg, sType); + new BufImgSurfaceData(scRaster.getDataBuffer(), bImg, sType, + scaleX, scaleY); bisd.initRaster(scRaster.getDataStorage(), scRaster.getDataOffset(0) * 2, 0, scRaster.getWidth(), @@ -284,11 +307,14 @@ public class BufImgSurfaceData extends SurfaceData { public static SurfaceData createDataBC(BufferedImage bImg, SurfaceType sType, - int primaryBank) { + int primaryBank, + double scaleX, double scaleY) + { ByteComponentRaster bcRaster = (ByteComponentRaster)bImg.getRaster(); BufImgSurfaceData bisd = - new BufImgSurfaceData(bcRaster.getDataBuffer(), bImg, sType); + new BufImgSurfaceData(bcRaster.getDataBuffer(), bImg, sType, + scaleX, scaleY); ColorModel cm = bImg.getColorModel(); IndexColorModel icm = ((cm instanceof IndexColorModel) ? (IndexColorModel) cm @@ -304,11 +330,14 @@ public class BufImgSurfaceData extends SurfaceData { } public static SurfaceData createDataBP(BufferedImage bImg, - SurfaceType sType) { + SurfaceType sType, + double scaleX, double scaleY) + { BytePackedRaster bpRaster = (BytePackedRaster)bImg.getRaster(); BufImgSurfaceData bisd = - new BufImgSurfaceData(bpRaster.getDataBuffer(), bImg, sType); + new BufImgSurfaceData(bpRaster.getDataBuffer(), bImg, sType, + scaleX, scaleY); ColorModel cm = bImg.getColorModel(); IndexColorModel icm = ((cm instanceof IndexColorModel) ? (IndexColorModel) cm @@ -350,15 +379,22 @@ public class BufImgSurfaceData extends SurfaceData { IndexColorModel icm); public BufImgSurfaceData(DataBuffer db, - BufferedImage bufImg, SurfaceType sType) + BufferedImage bufImg, + SurfaceType sType, + double scaleX, + double scaleY) { super(SunWritableRaster.stealTrackable(db), sType, bufImg.getColorModel()); this.bufImg = bufImg; + this.scaleX = scaleX; + this.scaleY = scaleY; } protected BufImgSurfaceData(SurfaceType surfaceType, ColorModel cm) { super(surfaceType, cm); + this.scaleX = 1; + this.scaleY = 1; } public void initSolidLoops() { @@ -395,7 +431,8 @@ public class BufImgSurfaceData extends SurfaceData { public synchronized GraphicsConfiguration getDeviceConfiguration() { if (graphicsConfig == null) { - graphicsConfig = BufferedImageGraphicsConfig.getConfig(bufImg); + graphicsConfig = BufferedImageGraphicsConfig + .getConfig(bufImg, scaleX, scaleY); } return graphicsConfig; } @@ -418,6 +455,16 @@ public class BufImgSurfaceData extends SurfaceData { return bufImg; } + @Override + public double getDefaultScaleX() { + return scaleX; + } + + @Override + public double getDefaultScaleY() { + return scaleY; + } + public static final class ICMColorData { private long pData = 0L; diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java b/jdk/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java index bddb83f6999..973eef76af2 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java @@ -45,19 +45,32 @@ public class BufferedImageGraphicsConfig extends GraphicsConfiguration { private static final int numconfigs = BufferedImage.TYPE_BYTE_BINARY; - private static BufferedImageGraphicsConfig configs[] = + private static BufferedImageGraphicsConfig standardConfigs[] = + new BufferedImageGraphicsConfig[numconfigs]; + private static BufferedImageGraphicsConfig scaledConfigs[] = new BufferedImageGraphicsConfig[numconfigs]; public static BufferedImageGraphicsConfig getConfig(BufferedImage bImg) { + return getConfig(bImg, 1, 1); + } + + public static BufferedImageGraphicsConfig getConfig(BufferedImage bImg, + double scaleX, + double scaleY) + { BufferedImageGraphicsConfig ret; int type = bImg.getType(); + + BufferedImageGraphicsConfig[] configs = (scaleX == 1 && scaleY == 1) + ? standardConfigs : scaledConfigs; + if (type > 0 && type < numconfigs) { ret = configs[type]; - if (ret != null) { + if (ret != null && ret.scaleX == scaleX && ret.scaleY == scaleY) { return ret; } } - ret = new BufferedImageGraphicsConfig(bImg, null); + ret = new BufferedImageGraphicsConfig(bImg, null, scaleX, scaleY); if (type > 0 && type < numconfigs) { configs[type] = ret; } @@ -67,8 +80,16 @@ public class BufferedImageGraphicsConfig GraphicsDevice gd; ColorModel model; Raster raster; + private final double scaleX; + private final double scaleY; public BufferedImageGraphicsConfig(BufferedImage bufImg, Component comp) { + this(bufImg, comp, 1, 1); + } + + public BufferedImageGraphicsConfig(BufferedImage bufImg, Component comp, + double scaleX, double scaleY) + { if (comp == null) { this.gd = new BufferedImageDevice(this); } else { @@ -77,6 +98,8 @@ public class BufferedImageGraphicsConfig } this.model = bufImg.getColorModel(); this.raster = bufImg.getRaster().createCompatibleWritableRaster(1, 1); + this.scaleX = scaleX; + this.scaleY = scaleY; } /** @@ -138,7 +161,7 @@ public class BufferedImageGraphicsConfig * For image buffers, this Transform will be the Identity transform. */ public AffineTransform getDefaultTransform() { - return new AffineTransform(); + return AffineTransform.getScaleInstance(scaleX, scaleY); } /** diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java b/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java index 97e201124a5..00c8911f696 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java @@ -233,8 +233,17 @@ public class SunVolatileImage extends VolatileImage * or a backup surface. */ public BufferedImage getBackupImage() { - return graphicsConfig.createCompatibleImage(getWidth(), getHeight(), - getTransparency()); + return getBackupImage(1, 1); + } + + /** + * This method creates a BufferedImage intended for use as a "snapshot" + * or a backup surface with the given horizontal and vertical scale factors. + */ + public BufferedImage getBackupImage(double scaleX, double scaleY) { + int w = (int) Math.ceil(getWidth() * scaleX); + int h = (int) Math.ceil(getHeight() * scaleY); + return graphicsConfig.createCompatibleImage(w, h, getTransparency()); } public BufferedImage getSnapshot() { diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java b/jdk/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java index 1b2db0f867f..6cf567cd5e8 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java @@ -290,16 +290,30 @@ public abstract class SurfaceManager { } /** - * Returns a scale factor of the image. This is utility method, which - * fetches information from the SurfaceData of the image. + * Returns a horizontal scale factor of the image. This is utility method, + * which fetches information from the SurfaceData of the image. * - * @see SurfaceData#getDefaultScale + * @see SurfaceData#getDefaultScaleX */ - public static int getImageScale(final Image img) { + public static double getImageScaleX(final Image img) { if (!(img instanceof VolatileImage)) { return 1; } final SurfaceManager sm = getManager(img); - return sm.getPrimarySurfaceData().getDefaultScale(); + return sm.getPrimarySurfaceData().getDefaultScaleX(); + } + + /** + * Returns a vertical scale factor of the image. This is utility method, + * which fetches information from the SurfaceData of the image. + * + * @see SurfaceData#getDefaultScaleY + */ + public static double getImageScaleY(final Image img) { + if (!(img instanceof VolatileImage)) { + return 1; + } + final SurfaceManager sm = getManager(img); + return sm.getPrimarySurfaceData().getDefaultScaleY(); } } diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java b/jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java index e3aa4fdb990..ef2abf1caf7 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java @@ -25,18 +25,16 @@ package sun.awt.image; -import java.awt.Color; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.ImageCapabilities; +import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.VolatileImage; import sun.awt.DisplayChangedListener; -import sun.awt.image.SunVolatileImage; import sun.java2d.SunGraphicsEnvironment; import sun.java2d.SurfaceData; -import sun.java2d.loops.CompositeType; import static sun.java2d.pipe.hw.AccelSurface.*; /** @@ -260,12 +258,16 @@ public abstract class VolatileSurfaceManager */ protected SurfaceData getBackupSurface() { if (sdBackup == null) { - BufferedImage bImg = vImg.getBackupImage(); + GraphicsConfiguration gc = vImg.getGraphicsConfig(); + AffineTransform tx = gc.getDefaultTransform(); + double scaleX = tx.getScaleX(); + double scaleY = tx.getScaleY(); + BufferedImage bImg = vImg.getBackupImage(scaleX, scaleY); // Sabotage the acceleration capabilities of the BufImg surface SunWritableRaster.stealTrackable(bImg .getRaster() .getDataBuffer()).setUntrackable(); - sdBackup = BufImgSurfaceData.createData(bImg); + sdBackup = BufImgSurfaceData.createData(bImg, scaleX, scaleY); } return sdBackup; } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 7ed0e3a7033..c7facaf62b6 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -61,7 +61,6 @@ import java.awt.FontMetrics; import java.awt.Rectangle; import java.text.AttributedCharacterIterator; import java.awt.Font; -import java.awt.Point; import java.awt.image.ImageObserver; import java.awt.Transparency; import java.awt.font.GlyphVector; @@ -99,6 +98,7 @@ import java.awt.image.MultiResolutionImage; import static java.awt.geom.AffineTransform.TYPE_FLIP; import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE; import static java.awt.geom.AffineTransform.TYPE_TRANSLATION; +import java.awt.image.VolatileImage; import sun.awt.image.MultiResolutionToolkitImage; import sun.awt.image.ToolkitImage; @@ -3086,30 +3086,50 @@ public final class SunGraphics2D } // end of text rendering methods - private boolean isHiDPIImage(final Image img) { - return (SurfaceManager.getImageScale(img) != 1) - || img instanceof MultiResolutionImage; - } + private Boolean drawHiDPIImage(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + Color bgcolor, ImageObserver observer, + AffineTransform xform) { - private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2, - int dy2, int sx1, int sy1, int sx2, int sy2, - Color bgcolor, ImageObserver observer) { + if (img instanceof VolatileImage) { + final SurfaceData sd = SurfaceManager.getManager(img) + .getPrimarySurfaceData(); + final double scaleX = sd.getDefaultScaleX(); + final double scaleY = sd.getDefaultScaleY(); + if (scaleX == 1 && scaleY == 1) { + return null; + } + sx1 = Region.clipScale(sx1, scaleX); + sx2 = Region.clipScale(sx2, scaleX); + sy1 = Region.clipScale(sy1, scaleY); + sy2 = Region.clipScale(sy2, scaleY); - if (SurfaceManager.getImageScale(img) != 1) { // Volatile Image - final int scale = SurfaceManager.getImageScale(img); - sx1 = Region.clipScale(sx1, scale); - sx2 = Region.clipScale(sx2, scale); - sy1 = Region.clipScale(sy1, scale); - sy2 = Region.clipScale(sy2, scale); - } else if (img instanceof MultiResolutionImage) { + AffineTransform tx = null; + if (xform != null) { + tx = new AffineTransform(transform); + transform(xform); + } + boolean result = scaleImage(img, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, + bgcolor, observer); + if (tx != null) { + transform.setTransform(tx); + invalidateTransform(); + } + return result; + } else if (resolutionVariantHint != SunHints.INTVAL_RESOLUTION_VARIANT_BASE + && (img instanceof MultiResolutionImage)) { // get scaled destination image size int width = img.getWidth(observer); int height = img.getHeight(observer); - Image resolutionVariant = getResolutionVariant( - (MultiResolutionImage) img, width, height, - dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2); + MultiResolutionImage mrImage = (MultiResolutionImage) img; + Image resolutionVariant = getResolutionVariant(mrImage, width, height, + dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, + xform); if (resolutionVariant != img && resolutionVariant != null) { // recalculate source region for the resolution variant @@ -3123,8 +3143,8 @@ public final class SunGraphics2D if (0 < width && 0 < height && 0 < rvWidth && 0 < rvHeight) { - float widthScale = ((float) rvWidth) / width; - float heightScale = ((float) rvHeight) / height; + double widthScale = ((double) rvWidth) / width; + double heightScale = ((double) rvHeight) / height; sx1 = Region.clipScale(sx1, widthScale); sy1 = Region.clipScale(sy1, heightScale); @@ -3133,10 +3153,29 @@ public final class SunGraphics2D observer = rvObserver; img = resolutionVariant; + + if (xform != null) { + assert dx1 == 0 && dy1 == 0; + assert dx2 == img.getWidth(observer); + assert dy2 == img.getHeight(observer); + AffineTransform renderTX = new AffineTransform(xform); + renderTX.scale(1 / widthScale, 1 / heightScale); + return transformImage(img, renderTX, observer); + } + + return scaleImage(img, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, + bgcolor, observer); } } } + return null; + } + private boolean scaleImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + Color bgcolor, ImageObserver observer) + { try { return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); @@ -3156,9 +3195,30 @@ public final class SunGraphics2D } } + private boolean transformImage(Image img, + AffineTransform xform, + ImageObserver observer) + { + try { + return imagepipe.transformImage(this, img, xform, observer); + } catch (InvalidPipeException e) { + try { + revalidateAll(); + return imagepipe.transformImage(this, img, xform, observer); + } catch (InvalidPipeException e2) { + // Still catching the exception; we are not yet ready to + // validate the surfaceData correctly. Fail for now and + // try again next time around. + return false; + } + } finally { + surfaceData.markDirty(); + } + } + private Image getResolutionVariant(MultiResolutionImage img, int srcWidth, int srcHeight, int dx1, int dy1, int dx2, int dy2, - int sx1, int sy1, int sx2, int sy2) { + int sx1, int sy1, int sx2, int sy2, AffineTransform xform) { if (srcWidth <= 0 || srcHeight <= 0) { return null; @@ -3171,7 +3231,16 @@ public final class SunGraphics2D return null; } - int type = transform.getType(); + AffineTransform tx; + + if (xform == null) { + tx = transform; + } else { + tx = new AffineTransform(transform); + tx.concatenate(xform); + } + + int type = tx.getType(); int dw = dx2 - dx1; int dh = dy2 - dy1; @@ -3198,13 +3267,13 @@ public final class SunGraphics2D destRegionWidth = dw; destRegionHeight = dh; } else if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP | TYPE_MASK_SCALE)) == 0) { - destRegionWidth = dw * transform.getScaleX(); - destRegionHeight = dh * transform.getScaleY(); + destRegionWidth = dw * tx.getScaleX(); + destRegionHeight = dh * tx.getScaleY(); } else { destRegionWidth = dw * Math.hypot( - transform.getScaleX(), transform.getShearY()); + tx.getScaleX(), tx.getShearY()); destRegionHeight = dh * Math.hypot( - transform.getShearX(), transform.getScaleY()); + tx.getShearX(), tx.getScaleY()); } destImageWidth = Math.abs(srcWidth * destRegionWidth / sw); destImageHeight = Math.abs(srcHeight * destRegionHeight / sh); @@ -3277,9 +3346,11 @@ public final class SunGraphics2D final int imgW = img.getWidth(null); final int imgH = img.getHeight(null); - if (isHiDPIImage(img)) { - return drawHiDPIImage(img, x, y, x + width, y + height, 0, 0, imgW, - imgH, bg, observer); + Boolean hidpiImageDrawn = drawHiDPIImage(img, x, y, x + width, y + height, + 0, 0, imgW, imgH, bg, observer, + null); + if (hidpiImageDrawn != null) { + return hidpiImageDrawn; } if (width == imgW && height == imgH) { @@ -3323,11 +3394,13 @@ public final class SunGraphics2D return true; } - if (isHiDPIImage(img)) { - final int imgW = img.getWidth(null); - final int imgH = img.getHeight(null); - return drawHiDPIImage(img, x, y, x + imgW, y + imgH, 0, 0, imgW, - imgH, bg, observer); + final int imgW = img.getWidth(null); + final int imgH = img.getHeight(null); + Boolean hidpiImageDrawn = drawHiDPIImage(img, x, y, x + imgW, y + imgH, + 0, 0, imgW, imgH, bg, observer, + null); + if (hidpiImageDrawn != null) { + return hidpiImageDrawn; } try { @@ -3378,9 +3451,12 @@ public final class SunGraphics2D return true; } - if (isHiDPIImage(img)) { - return drawHiDPIImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, - bgcolor, observer); + Boolean hidpiImageDrawn = drawHiDPIImage(img, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, + bgcolor, observer, null); + + if (hidpiImageDrawn != null) { + return hidpiImageDrawn; } if (((sx2 - sx1) == (dx2 - dx1)) && @@ -3461,33 +3537,16 @@ public final class SunGraphics2D return drawImage(img, 0, 0, null, observer); } - if (isHiDPIImage(img)) { - final int w = img.getWidth(null); - final int h = img.getHeight(null); - final AffineTransform tx = new AffineTransform(transform); - transform(xform); - boolean result = drawHiDPIImage(img, 0, 0, w, h, 0, 0, w, h, null, - observer); - transform.setTransform(tx); - invalidateTransform(); - return result; + final int w = img.getWidth(null); + final int h = img.getHeight(null); + Boolean hidpiImageDrawn = drawHiDPIImage(img, 0, 0, w, h, 0, 0, w, h, + null, observer, xform); + + if (hidpiImageDrawn != null) { + return hidpiImageDrawn; } - try { - return imagepipe.transformImage(this, img, xform, observer); - } catch (InvalidPipeException e) { - try { - revalidateAll(); - return imagepipe.transformImage(this, img, xform, observer); - } catch (InvalidPipeException e2) { - // Still catching the exception; we are not yet ready to - // validate the surfaceData correctly. Fail for now and - // try again next time around. - return false; - } - } finally { - surfaceData.markDirty(); - } + return transformImage(img, xform, observer); } public void drawImage(BufferedImage bImg, diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java b/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java index 6a170c95d88..679f801d2e4 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java @@ -66,6 +66,8 @@ import sun.font.FontManager; import sun.font.FontManagerFactory; import sun.font.FontManagerForSGE; import sun.font.NativeFont; +import java.security.AccessController; +import sun.security.action.GetPropertyAction; /** * This is an implementation of a GraphicsEnvironment object for the @@ -80,6 +82,15 @@ public abstract class SunGraphicsEnvironment extends GraphicsEnvironment public static boolean isOpenSolaris; private static Font defaultFont; + private static final boolean uiScaleEnabled; + private static final double debugScale; + + static { + uiScaleEnabled = "true".equals(AccessController.doPrivileged( + new GetPropertyAction("sun.java2d.uiScale.enabled", "true"))); + debugScale = uiScaleEnabled ? getScaleFactor("sun.java2d.uiScale") : -1; + } + public SunGraphicsEnvironment() { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { @@ -341,4 +352,41 @@ public abstract class SunGraphicsEnvironment extends GraphicsEnvironment public boolean isFlipStrategyPreferred(ComponentPeer peer) { return false; } + + public static boolean isUIScaleEnabled() { + return uiScaleEnabled; + } + + public static double getDebugScale() { + return debugScale; + } + + public static double getScaleFactor(String propertyName) { + + String scaleFactor = AccessController.doPrivileged( + new GetPropertyAction(propertyName, "-1")); + + if (scaleFactor == null || scaleFactor.equals("-1")) { + return -1; + } + + try { + double units = 1.0; + + if (scaleFactor.endsWith("x")) { + scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1); + } else if (scaleFactor.endsWith("dpi")) { + units = 96; + scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 3); + } else if (scaleFactor.endsWith("%")) { + units = 100; + scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1); + } + + double scale = Double.parseDouble(scaleFactor); + return scale <= 0 ? -1 : scale / units; + } catch (NumberFormatException ignored) { + return -1; + } + } } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java b/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java index 81794b5d490..8250d09a43d 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java @@ -1059,12 +1059,22 @@ public abstract class SurfaceData public abstract Object getDestination(); /** - * Returns default scale factor of the destination surface. Scale factor - * describes the mapping between virtual and physical coordinates of the + * Returns default horizontal scale factor of the destination surface. Scale + * factor describes the mapping between virtual and physical coordinates of the * SurfaceData. If the scale is 2 then virtual pixel coordinates need to be * doubled for physical pixels. */ - public int getDefaultScale() { + public double getDefaultScaleX() { + return 1; + } + + /** + * Returns default vertical scale factor of the destination surface. Scale + * factor describes the mapping between virtual and physical coordinates of the + * SurfaceData. If the scale is 2 then virtual pixel coordinates need to be + * doubled for physical pixels. + */ + public double getDefaultScaleY() { return 1; } } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java b/jdk/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java index cfa130b8199..a7e181e9d2a 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java @@ -736,9 +736,10 @@ public class DrawImage implements DrawImagePipe atfm.scale(m00, m11); atfm.translate(srcX-sx1, srcY-sy1); - final int scale = SurfaceManager.getImageScale(img); - final int imgW = img.getWidth(null) * scale; - final int imgH = img.getHeight(null) * scale; + final double scaleX = SurfaceManager.getImageScaleX(img); + final double scaleY = SurfaceManager.getImageScaleY(img); + final int imgW = (int) Math.ceil(img.getWidth(null) * scaleX); + final int imgH = (int) Math.ceil(img.getHeight(null) * scaleY); srcW += srcX; srcH += srcY; // Make sure we are not out of bounds diff --git a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java b/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java index 2ec0d1b1944..4773934e39e 100644 --- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java +++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java @@ -106,7 +106,7 @@ public class Win32GraphicsConfig extends GraphicsConfiguration /** * Return the graphics device associated with this configuration. */ - public GraphicsDevice getDevice() { + public Win32GraphicsDevice getDevice() { return screen; } @@ -182,7 +182,9 @@ public class Win32GraphicsConfig extends GraphicsConfiguration * For image buffers, this Transform will be the Identity transform. */ public AffineTransform getDefaultTransform() { - return new AffineTransform(); + double scaleX = screen.getDefaultScaleX(); + double scaleY = screen.getDefaultScaleY(); + return AffineTransform.getScaleInstance(scaleX, scaleY); } /** diff --git a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java b/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java index 85c1453b196..e7548a6f169 100644 --- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java +++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java @@ -37,13 +37,19 @@ import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; +import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.util.ArrayList; import java.util.Vector; import java.awt.peer.WindowPeer; +import java.security.AccessController; import sun.awt.windows.WWindowPeer; +import sun.java2d.SunGraphicsEnvironment; import sun.java2d.opengl.WGLGraphicsConfig; import sun.java2d.windows.WindowsFlags; +import sun.security.action.GetPropertyAction; +import static sun.awt.Win32GraphicsEnvironment.debugScaleX; +import static sun.awt.Win32GraphicsEnvironment.debugScaleY; /** * This is an implementation of a GraphicsDevice object for a single @@ -81,6 +87,9 @@ public class Win32GraphicsDevice extends GraphicsDevice implements // activation/deactivation listener for the full-screen window private WindowListener fsWindowListener; + private float scaleX; + private float scaleY; + static { // 4455041 - Even when ddraw is disabled, ddraw.dll is loaded when @@ -97,6 +106,10 @@ public class Win32GraphicsDevice extends GraphicsDevice implements private static native void initIDs(); native void initDevice(int screen); + native void initNativeScale(int screen); + native void setNativeScale(int screen, float scaleX, float scaleY); + native float getNativeScaleX(int screen); + native float getNativeScaleY(int screen); public Win32GraphicsDevice(int screennum) { this.screen = screennum; @@ -109,6 +122,7 @@ public class Win32GraphicsDevice extends GraphicsDevice implements valid = true; initDevice(screennum); + initScaleFactors(); } /** @@ -128,6 +142,31 @@ public class Win32GraphicsDevice extends GraphicsDevice implements return screen; } + public float getDefaultScaleX() { + return scaleX; + } + + public float getDefaultScaleY() { + return scaleY; + } + + private void initScaleFactors() { + if (SunGraphicsEnvironment.isUIScaleEnabled()) { + if (debugScaleX > 0 && debugScaleY > 0) { + scaleX = debugScaleX; + scaleY = debugScaleY; + setNativeScale(screen, scaleX, scaleY); + } else { + initNativeScale(screen); + scaleX = getNativeScaleX(screen); + scaleY = getNativeScaleY(screen); + } + } else { + scaleX = 1; + scaleY = 1; + } + } + /** * Returns whether this is a valid devicie. Device can become * invalid as a result of device removal event. @@ -486,6 +525,7 @@ public class Win32GraphicsDevice extends GraphicsDevice implements configs = null; // pass on to all top-level windows on this display topLevels.notifyListeners(); + initScaleFactors(); } /** diff --git a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java index d98f2afeb5b..464541c2b53 100644 --- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java +++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java @@ -51,6 +51,9 @@ import sun.java2d.windows.WindowsFlags; public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment { + static final float debugScaleX; + static final float debugScaleY; + static { // Ensure awt is loaded already. Also, this forces static init // of WToolkit and Toolkit, which we depend upon @@ -61,6 +64,21 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment { // Install correct surface manager factory. SurfaceManagerFactory.setInstance(new WindowsSurfaceManagerFactory()); + + double sx = -1; + double sy = -1; + if (isUIScaleEnabled()) { + sx = getScaleFactor("sun.java2d.win.uiScaleX"); + sy = getScaleFactor("sun.java2d.win.uiScaleY"); + if (sx <= 0 || sy <= 0) { + double s = getDebugScale(); + sx = s; + sy = s; + } + } + + debugScaleX = (float) sx; + debugScaleY = (float) sy; } /** diff --git a/jdk/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java b/jdk/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java index 5605ecd04bd..12885ad2a61 100644 --- a/jdk/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/jdk/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java @@ -294,6 +294,12 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, synchronized native void reshapeFrame(int x, int y, int width, int height); + native Dimension getNativeWindowSize(); + + public Dimension getScaledWindowSize() { + return getNativeWindowSize(); + } + public boolean requestWindowFocus(CausedFocusEvent.Cause cause) { if (!focusAllowedFor()) { return false; @@ -490,8 +496,7 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, } // get current GD - Win32GraphicsDevice oldDev = (Win32GraphicsDevice)winGraphicsConfig - .getDevice(); + Win32GraphicsDevice oldDev = winGraphicsConfig.getDevice(); Win32GraphicsDevice newDev; GraphicsDevice devs[] = GraphicsEnvironment diff --git a/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java b/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java index 4ff6f4c54c5..c6b25970951 100644 --- a/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java +++ b/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java @@ -63,9 +63,12 @@ import static sun.java2d.d3d.D3DContext.D3DContextCaps.*; import static sun.java2d.pipe.hw.ExtendedBufferCapabilities.VSyncType.*; import sun.java2d.pipe.hw.ExtendedBufferCapabilities.VSyncType; import java.awt.BufferCapabilities.FlipContents; +import java.awt.Dimension; import java.awt.Window; +import java.awt.geom.AffineTransform; import sun.awt.SunToolkit; import sun.awt.image.SunVolatileImage; +import sun.awt.windows.WWindowPeer; import sun.java2d.ScreenUpdateManager; import sun.java2d.StateTracker; import sun.java2d.SurfaceDataProxy; @@ -162,6 +165,8 @@ public class D3DSurfaceData extends SurfaceData implements AccelSurface { private int type; private int width, height; + private final double scaleX; + private final double scaleY; // these fields are set from the native code when the surface is // initialized private int nativeWidth, nativeHeight; @@ -218,16 +223,29 @@ public class D3DSurfaceData extends SurfaceData implements AccelSurface { { super(getCustomSurfaceType(type), cm); this.graphicsDevice = gc.getD3DDevice(); + this.scaleX = type == TEXTURE ? 1 : graphicsDevice.getDefaultScaleX(); + this.scaleY = type == TEXTURE ? 1 : graphicsDevice.getDefaultScaleY(); this.peer = peer; this.type = type; - this.width = width; - this.height = height; + + if (scaleX == 1 && scaleY == 1) { + this.width = width; + this.height = height; + } else if (peer instanceof WWindowPeer) { + Dimension scaledSize = ((WWindowPeer) peer).getScaledWindowSize(); + this.width = scaledSize.width; + this.height = scaledSize.height; + } else { + this.width = (int) Math.ceil(width * scaleX); + this.height = (int) Math.ceil(height * scaleY); + } + this.offscreenImage = image; this.backBuffersNum = numBackBuffers; this.swapEffect = swapEffect; this.syncType = vSyncType; - initOps(graphicsDevice.getScreen(), width, height); + initOps(graphicsDevice.getScreen(), this.width, this.height); if (type == WINDOW) { // we put the surface into the "lost" // state; it will be restored by the D3DScreenUpdateManager @@ -240,6 +258,16 @@ public class D3DSurfaceData extends SurfaceData implements AccelSurface { setBlitProxyKey(gc.getProxyKey()); } + @Override + public double getDefaultScaleX() { + return scaleX; + } + + @Override + public double getDefaultScaleY() { + return scaleY; + } + @Override public SurfaceDataProxy makeProxyFor(SurfaceData srcData) { return D3DSurfaceDataProxy. @@ -777,8 +805,12 @@ public class D3DSurfaceData extends SurfaceData implements AccelSurface { public Rectangle getBounds() { if (type == FLIP_BACKBUFFER || type == WINDOW) { + double scaleX = getDefaultScaleX(); + double scaleY = getDefaultScaleY(); Rectangle r = peer.getBounds(); r.x = r.y = 0; + r.width = (int) Math.ceil(r.width * scaleX); + r.height = (int) Math.ceil(r.height * scaleY); return r; } else { return new Rectangle(width, height); diff --git a/jdk/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java b/jdk/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java index b415cf8f36d..d27b7d1cc6b 100644 --- a/jdk/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java +++ b/jdk/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java @@ -31,8 +31,10 @@ import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Rectangle; +import java.awt.geom.AffineTransform; import java.awt.image.ColorModel; import sun.awt.SunToolkit; +import sun.awt.Win32GraphicsDevice; import sun.awt.windows.WComponentPeer; import sun.java2d.SurfaceData; @@ -40,6 +42,8 @@ public abstract class WGLSurfaceData extends OGLSurfaceData { protected WComponentPeer peer; private WGLGraphicsConfig graphicsConfig; + protected double scaleX = 1; + protected double scaleY = 1; private native void initOps(long pConfigInfo, WComponentPeer peer, long hwnd); @@ -50,6 +54,9 @@ public abstract class WGLSurfaceData extends OGLSurfaceData { super(gc, cm, type); this.peer = peer; this.graphicsConfig = gc; + Win32GraphicsDevice device = gc.getDevice(); + this.scaleX = type == TEXTURE ? 1 : device.getDefaultScaleX(); + this.scaleY = type == TEXTURE ? 1 : device.getDefaultScaleY(); long pConfigInfo = gc.getNativeConfigInfo(); long hwnd = peer != null ? peer.getHWnd() : 0L; @@ -57,6 +64,16 @@ public abstract class WGLSurfaceData extends OGLSurfaceData { initOps(pConfigInfo, peer, hwnd); } + @Override + public double getDefaultScaleX() { + return scaleX; + } + + @Override + public double getDefaultScaleY() { + return scaleY; + } + public GraphicsConfiguration getDeviceConfiguration() { return graphicsConfig; } @@ -148,6 +165,8 @@ public abstract class WGLSurfaceData extends OGLSurfaceData { public Rectangle getBounds() { Rectangle r = peer.getBounds(); r.x = r.y = 0; + r.width = (int) Math.ceil(r.width * scaleX); + r.height = (int) Math.ceil(r.height * scaleY); return r; } @@ -208,11 +227,11 @@ public abstract class WGLSurfaceData extends OGLSurfaceData { { super(peer, gc, cm, type); - this.width = width; - this.height = height; + this.width = (int) Math.ceil(width * scaleX); + this.height = (int) Math.ceil(height * scaleY); offscreenImage = image; - initSurface(width, height); + initSurface(this.width, this.height); } public SurfaceData getReplacement() { @@ -222,6 +241,8 @@ public abstract class WGLSurfaceData extends OGLSurfaceData { public Rectangle getBounds() { if (type == FLIP_BACKBUFFER) { Rectangle r = peer.getBounds(); + r.width = (int) Math.ceil(r.width * scaleX); + r.height = (int) Math.ceil(r.height * scaleY); r.x = r.y = 0; return r; } else { diff --git a/jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java b/jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java index 04ba1464001..2ba2b11aa59 100644 --- a/jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java +++ b/jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java @@ -28,6 +28,7 @@ package sun.java2d.windows; import java.awt.Rectangle; import java.awt.GraphicsConfiguration; import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DirectColorModel; @@ -77,6 +78,9 @@ public class GDIWindowSurfaceData extends SurfaceData { private static native void initIDs(Class xorComp); + private final double scaleX; + private final double scaleY; + static { initIDs(XORComposite.class); if (WindowsFlags.isGdiBlitEnabled()) { @@ -265,13 +269,23 @@ public class GDIWindowSurfaceData extends SurfaceData { this.graphicsConfig = (Win32GraphicsConfig) peer.getGraphicsConfiguration(); this.solidloops = graphicsConfig.getSolidLoops(sType); - - Win32GraphicsDevice gd = - (Win32GraphicsDevice)graphicsConfig.getDevice(); + Win32GraphicsDevice gd = graphicsConfig.getDevice(); + scaleX = gd.getDefaultScaleX(); + scaleY = gd.getDefaultScaleY(); initOps(peer, depth, rMask, gMask, bMask, gd.getScreen()); setBlitProxyKey(graphicsConfig.getProxyKey()); } + @Override + public double getDefaultScaleX() { + return scaleX; + } + + @Override + public double getDefaultScaleY() { + return scaleY; + } + /** * {@inheritDoc} * @@ -288,6 +302,8 @@ public class GDIWindowSurfaceData extends SurfaceData { public Rectangle getBounds() { Rectangle r = peer.getBounds(); r.x = r.y = 0; + r.width = (int) Math.ceil(r.width * scaleX); + r.height = (int) Math.ceil(r.height * scaleY); return r; } diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp index b109f28ded8..e43f6ea3f5f 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp @@ -94,12 +94,21 @@ Java_sun_awt_DefaultMouseInfoPeer_fillPointWithCoords(JNIEnv *env, jclass cls, j pointClass = (jclass)env->NewGlobalRef(pointClassLocal); env->DeleteLocalRef(pointClassLocal); } + + int screen = AwtWin32GraphicsDevice::GetDefaultDeviceIndex(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(screen); + xID = env->GetFieldID(pointClass, "x", "I"); CHECK_NULL_RETURN(xID, (jint)0); yID = env->GetFieldID(pointClass, "y", "I"); CHECK_NULL_RETURN(yID, (jint)0); - env->SetIntField(point, xID, pt.x); - env->SetIntField(point, yID, pt.y); + + int x = (device == NULL) ? pt.x : device->ScaleDownX(pt.x); + int y = (device == NULL) ? pt.y : device->ScaleDownY(pt.y); + + env->SetIntField(point, xID, x); + env->SetIntField(point, yID, y); // Always return 0 on Windows: we assume there's always a // virtual screen device used. diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp index 6a597475148..be1c41e840f 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp @@ -206,9 +206,10 @@ int AwtChoice::GetDropDownHeight() int itemHeight =(int)::SendMessage(GetHWnd(), CB_GETITEMHEIGHT, (UINT)0,0); int numItemsToShow = (int)::SendMessage(GetHWnd(), CB_GETCOUNT, 0,0); numItemsToShow = min(MINIMUM_NUMBER_OF_VISIBLE_ITEMS, numItemsToShow); + // drop-down height snaps to nearest line, so add a // fudge factor of 1/2 line to ensure last line shows - return itemHeight*numItemsToShow + itemHeight/2; + return ScaleDownY(itemHeight * numItemsToShow + itemHeight / 2); } // get the height of the field portion of the combobox @@ -221,7 +222,7 @@ int AwtChoice::GetFieldHeight() // Win 4.x (3d edge) vs 3.x (1 pixel line) borderHeight = ::GetSystemMetrics(SM_CYEDGE); fieldHeight += borderHeight*2; - return fieldHeight; + return ScaleDownY(fieldHeight); } // gets the total height of the combobox, including drop down @@ -325,8 +326,8 @@ void AwtChoice::Reshape(int x, int y, int w, int h) * Fix: Set the Choice to its actual size in the component. */ ::GetClientRect(GetHWnd(), &rc); - env->SetIntField(target, AwtComponent::widthID, (jint)rc.right); - env->SetIntField(target, AwtComponent::heightID, (jint)rc.bottom); + env->SetIntField(target, AwtComponent::widthID, ScaleDownX(rc.right)); + env->SetIntField(target, AwtComponent::heightID, ScaleDownY(rc.bottom)); env->DeleteLocalRef(target); env->DeleteLocalRef(parent); diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp index b234ec0221b..a36db45b72f 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp @@ -963,6 +963,12 @@ void AwtComponent::Reshape(int x, int y, int w, int h) ::MapWindowPoints(HWND_DESKTOP, ::GetParent(GetHWnd()), (LPPOINT)&rc, 2); DTRACE_PRINTLN4("AwtComponent::Reshape from %d, %d, %d, %d", rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top); #endif + + x = ScaleUpX(x); + y = ScaleUpY(y); + w = ScaleUpX(w); + h = ScaleUpY(h); + AwtWindow* container = GetContainer(); AwtComponent* parent = GetParent(); if (container != NULL && container == parent) { @@ -2212,8 +2218,11 @@ void AwtComponent::PaintUpdateRgn(const RECT *insets) } for(i = 0; i < 2; i++) { if (un[i] != 0) { - DoCallback("handleExpose", "(IIII)V", un[i]->left, un[i]->top, - un[i]->right-un[i]->left, un[i]->bottom-un[i]->top); + DoCallback("handleExpose", "(IIII)V", + ScaleDownX(un[i]->left), + ScaleDownY(un[i]->top), + ScaleDownX(un[i]->right - un[i]->left), + ScaleDownY(un[i]->bottom - un[i]->top)); } } delete [] buffer; @@ -4608,6 +4617,34 @@ void AwtComponent::FillAlpha(void *bitmapBits, SIZE &size, BYTE alpha) } } +int AwtComponent::ScaleUpX(int x) { + int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? x : device->ScaleUpX(x); +} + +int AwtComponent::ScaleUpY(int y) { + int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? y : device->ScaleUpY(y); +} + +int AwtComponent::ScaleDownX(int x) { + int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? x : device->ScaleDownX(x); +} + +int AwtComponent::ScaleDownY(int y) { + int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? y : device->ScaleDownY(y); +} + jintArray AwtComponent::CreatePrintedPixels(SIZE &loc, SIZE &size, int alpha) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); @@ -4901,8 +4938,9 @@ void AwtComponent::SendMouseEvent(jint id, jlong when, jint x, jint y, jobject mouseEvent = env->NewObject(mouseEventCls, mouseEventConst, target, id, when, modifiers, - x+insets.left, y+insets.top, - xAbs, yAbs, + ScaleDownX(x + insets.left), + ScaleDownY(y + insets.top), + ScaleDownX(xAbs), ScaleDownY(yAbs), clickCount, popupTrigger, button); if (safe_ExceptionOccurred(env)) { @@ -4969,8 +5007,10 @@ AwtComponent::SendMouseWheelEvent(jint id, jlong when, jint x, jint y, mouseWheelEventConst, target, id, when, modifiers, - x+insets.left, y+insets.top, - xAbs, yAbs, + ScaleDownX(x + insets.left), + ScaleDownY(y + insets.top), + ScaleDownX(xAbs), + ScaleDownY(yAbs), clickCount, popupTrigger, scrollType, scrollAmount, roundedWheelRotation, preciseWheelRotation); @@ -5476,7 +5516,8 @@ jobject AwtComponent::_GetLocationOnScreen(void *param) RECT rect; VERIFY(::GetWindowRect(p->GetHWnd(),&rect)); result = JNU_NewObjectByName(env, "java/awt/Point", "(II)V", - rect.left, rect.top); + p->ScaleDownX(rect.left), + p->ScaleDownY(rect.top)); } ret: env->DeleteGlobalRef(self); @@ -7064,6 +7105,11 @@ void AwtComponent::VerifyState() target = parent; } + x = ScaleUpX(x); + y = ScaleUpY(y); + width = ScaleUpX(width); + height = ScaleUpY(height); + // Test whether component's bounds match the native window's RECT rect; VERIFY(::GetWindowRect(GetHWnd(), &rect)); @@ -7256,5 +7302,4 @@ void ReleaseDCList(HWND hwnd, DCList &list) { removedDCs = removedDCs->next; delete tmpDCList; } -} - +} \ No newline at end of file diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.h b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.h index f09b41f5b54..83c6d8b8a4e 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.h +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.h @@ -746,6 +746,11 @@ protected: virtual void FillBackground(HDC hMemoryDC, SIZE &size); virtual void FillAlpha(void *bitmapBits, SIZE &size, BYTE alpha); + int ScaleUpX(int x); + int ScaleUpY(int y); + int ScaleDownX(int x); + int ScaleDownY(int y); + private: /* A bitmask keeps the button's numbers as MK_LBUTTON, MK_MBUTTON, MK_RBUTTON * which are allowed to diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp index 163283f0ef7..a8d7fd1bcb6 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp @@ -398,6 +398,38 @@ static void strip_tail(wchar_t* text, wchar_t* tail) { // strips tail and any po } +static int ScaleUpX(float x) { + int deviceIndex = AwtWin32GraphicsDevice::DeviceIndexForWindow( + ::GetDesktopWindow()); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(deviceIndex); + return device == NULL ? x : device->ScaleUpX(x); +} + +static int ScaleUpY(int y) { + int deviceIndex = AwtWin32GraphicsDevice::DeviceIndexForWindow( + ::GetDesktopWindow()); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(deviceIndex); + return device == NULL ? y : device->ScaleUpY(y); +} + +static int ScaleDownX(int x) { + int deviceIndex = AwtWin32GraphicsDevice::DeviceIndexForWindow( + ::GetDesktopWindow()); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(deviceIndex); + return device == NULL ? x : device->ScaleDownX(x); +} + +static int ScaleDownY(int y) { + int deviceIndex = AwtWin32GraphicsDevice::DeviceIndexForWindow( + ::GetDesktopWindow()); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(deviceIndex); + return device == NULL ? y : device->ScaleDownY(y); +} + static HFONT CreateHFont_sub(LPCWSTR name, int style, int height, int angle=0, float awScale=1.0f) { @@ -424,7 +456,7 @@ static HFONT CreateHFont_sub(LPCWSTR name, int style, int height, logFont.lfUnderline = 0;//(style & java_awt_Font_UNDERLINE) != 0; // Get point size - logFont.lfHeight = -height; + logFont.lfHeight = ScaleUpY(-height); // Set font name WCHAR tmpname[80]; @@ -451,7 +483,7 @@ static HFONT CreateHFont_sub(LPCWSTR name, int style, int height, VERIFY(::DeleteObject(oldFont)); } avgWidth = tm.tmAveCharWidth; - logFont.lfWidth = (LONG)((fabs)(avgWidth*awScale)); + logFont.lfWidth = (LONG) ScaleUpX((fabs) (avgWidth * awScale)); hFont = ::CreateFontIndirect(&logFont); DASSERT(hFont != NULL); VERIFY(::ReleaseDC(0, hDC)); @@ -535,19 +567,20 @@ void AwtFont::LoadMetrics(JNIEnv *env, jobject fontMetrics) int ascent = metrics.tmAscent; int descent = metrics.tmDescent; int leading = metrics.tmExternalLeading; - env->SetIntField(fontMetrics, AwtFont::ascentID, ascent); - env->SetIntField(fontMetrics, AwtFont::descentID, descent); - env->SetIntField(fontMetrics, AwtFont::leadingID, leading); - env->SetIntField(fontMetrics, AwtFont::heightID, metrics.tmAscent + - metrics.tmDescent + leading); - env->SetIntField(fontMetrics, AwtFont::maxAscentID, ascent); - env->SetIntField(fontMetrics, AwtFont::maxDescentID, descent); + + env->SetIntField(fontMetrics, AwtFont::ascentID, ScaleDownY(ascent)); + env->SetIntField(fontMetrics, AwtFont::descentID, ScaleDownY(descent)); + env->SetIntField(fontMetrics, AwtFont::leadingID, ScaleDownX(leading)); + env->SetIntField(fontMetrics, AwtFont::heightID, + ScaleDownY(metrics.tmAscent + metrics.tmDescent + leading)); + env->SetIntField(fontMetrics, AwtFont::maxAscentID, ScaleDownY(ascent)); + env->SetIntField(fontMetrics, AwtFont::maxDescentID, ScaleDownY(descent)); int maxHeight = ascent + descent + leading; - env->SetIntField(fontMetrics, AwtFont::maxHeightID, maxHeight); + env->SetIntField(fontMetrics, AwtFont::maxHeightID, ScaleDownY(maxHeight)); int maxAdvance = metrics.tmMaxCharWidth; - env->SetIntField(fontMetrics, AwtFont::maxAdvanceID, maxAdvance); + env->SetIntField(fontMetrics, AwtFont::maxAdvanceID, ScaleDownX(maxAdvance)); awtFont->m_overhang = metrics.tmOverhang; @@ -818,6 +851,7 @@ Java_sun_awt_windows_WFontMetrics_stringWidth(JNIEnv *env, jobject self, jobject font = env->GetObjectField(self, AwtFont::fontID); long ret = AwtFont::getMFStringWidth(hDC, font, str); + ret = ScaleDownX(ret); VERIFY(::ReleaseDC(0, hDC)); return ret; @@ -924,7 +958,7 @@ Java_sun_awt_windows_WFontMetrics_bytesWidth(JNIEnv *env, jobject self, } env->ReleasePrimitiveArrayCritical(str, pStrBody, 0); - return result; + return ScaleDownX(result); CATCH_BAD_ALLOC_RET(0); } diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp index 7739af44b7a..0528148e23e 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp @@ -80,6 +80,13 @@ void AwtRobot::MouseMove( jint x, jint y) (PVOID)newSpeed, SPIF_SENDCHANGE); + int primaryIndex = AwtWin32GraphicsDevice::GetDefaultDeviceIndex(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(primaryIndex); + + x = (device == NULL) ? x : device->ScaleUpX(x); + y = (device == NULL) ? y : device->ScaleUpY(y); + POINT curPos; ::GetCursorPos(&curPos); x -= curPos.x; @@ -217,11 +224,24 @@ void AwtRobot::GetRGBPixels(jint x, jint y, jint width, jint height, jintArray p AwtWin32GraphicsDevice::SelectPalette(hdcMem, primaryIndex); AwtWin32GraphicsDevice::RealizePalette(hdcMem, primaryIndex); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(primaryIndex); + int sWidth = (device == NULL) ? width : device->ScaleUpX(width); + int sHeight = (device == NULL) ? height : device->ScaleUpY(height); + // copy screen image to offscreen bitmap // CAPTUREBLT flag is required to capture WS_EX_LAYERED windows' contents // correctly on Win2K/XP - VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y, - SRCCOPY|CAPTUREBLT) != 0); + if (width == sWidth && height == sHeight) { + VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y, + SRCCOPY | CAPTUREBLT) != 0); + } else { + int sX = (device == NULL) ? x : device->ScaleUpX(x); + int sY = (device == NULL) ? y : device->ScaleUpY(y); + VERIFY(::StretchBlt(hdcMem, 0, 0, width, height, + hdcScreen, sX, sY, sWidth, sHeight, + SRCCOPY | CAPTUREBLT) != 0); + } static const int BITS_PER_PIXEL = 32; static const int BYTES_PER_PIXEL = BITS_PER_PIXEL/8; diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp index dcb4ea71f8d..3dedda934c9 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp @@ -2355,8 +2355,13 @@ Java_sun_awt_windows_WToolkit_getScreenWidth(JNIEnv *env, jobject self) { TRY; - return ::GetSystemMetrics(SM_CXSCREEN); + int width = ::GetSystemMetrics(SM_CXSCREEN); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice( + AwtWin32GraphicsDevice::GetDefaultDeviceIndex()); + + return (device == NULL) ? width : device->ScaleDownX(width); CATCH_BAD_ALLOC_RET(0); } @@ -2370,7 +2375,12 @@ Java_sun_awt_windows_WToolkit_getScreenHeight(JNIEnv *env, jobject self) { TRY; - return ::GetSystemMetrics(SM_CYSCREEN); + int height = ::GetSystemMetrics(SM_CYSCREEN); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice( + AwtWin32GraphicsDevice::GetDefaultDeviceIndex()); + + return (device == NULL) ? height : device->ScaleDownY(height); CATCH_BAD_ALLOC_RET(0); } diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp index 918ed08b626..7d13fbe2d87 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp @@ -95,19 +95,31 @@ JNIEXPORT jobject JNICALL mid = env->GetMethodID(clazz, "", "(IIII)V"); if (mid != 0) { RECT rRW = {0, 0, 0, 0}; + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(screen); + if (TRUE == MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW)) { - bounds = env->NewObject(clazz, mid, - rRW.left, rRW.top, - rRW.right - rRW.left, - rRW.bottom - rRW.top); + + int x = (device == NULL) ? rRW.left : device->ScaleDownX(rRW.left); + int y = (device == NULL) ? rRW.top : device->ScaleDownY(rRW.top); + int w = (device == NULL) ? rRW.right - rRW.left + : device->ScaleDownX(rRW.right - rRW.left); + int h = (device == NULL) ? rRW.bottom - rRW.top + : device->ScaleDownY(rRW.bottom - rRW.top); + + bounds = env->NewObject(clazz, mid, x, y, w, h); + } else { // 4910760 - don't return a null bounds, return the bounds of the // primary screen + int w = ::GetSystemMetrics(SM_CXSCREEN); + int h = ::GetSystemMetrics(SM_CYSCREEN); + bounds = env->NewObject(clazz, mid, 0, 0, - ::GetSystemMetrics(SM_CXSCREEN), - ::GetSystemMetrics(SM_CYSCREEN)); + device == NULL ? w : device->ScaleDownX(w), + device == NULL ? h : device->ScaleDownY(h)); } if (safe_ExceptionOccurred(env)) { return 0; diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp index 825a69c8c42..95f1b73e930 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp @@ -49,6 +49,12 @@ #include "dither.h" #include "img_util_md.h" #include "Devices.h" +#include +#pragma comment(lib, "d2d1") + +#ifndef MDT_Effective_DPI +#define MDT_Effective_DPI 0 +#endif uns_ordered_dither_array img_oda_alpha; @@ -74,6 +80,8 @@ AwtWin32GraphicsDevice::AwtWin32GraphicsDevice(int screen, { this->screen = screen; this->devicesArray = arr; + this->scaleX = 1; + this->scaleY = 1; javaDevice = NULL; colorData = new ImgColorData; colorData->grayscale = GS_NOTGRAY; @@ -616,6 +624,104 @@ void AwtWin32GraphicsDevice::SetJavaDevice(JNIEnv *env, jobject objPtr) javaDevice = env->NewWeakGlobalRef(objPtr); } +/** + * Sets horizontal and vertical scale factors + */ +void AwtWin32GraphicsDevice::SetScale(float sx, float sy) +{ + scaleX = sx; + scaleY = sy; +} + +int AwtWin32GraphicsDevice::ScaleUpX(int x) +{ + return (int)ceil(x * scaleX); +} + +int AwtWin32GraphicsDevice::ScaleUpY(int y) +{ + return (int)ceil(y * scaleY); +} + +int AwtWin32GraphicsDevice::ScaleDownX(int x) +{ + return (int)ceil(x / scaleX); +} + +int AwtWin32GraphicsDevice::ScaleDownY(int y) +{ + return (int)ceil(y / scaleY); +} + +void AwtWin32GraphicsDevice::InitDesktopScales() +{ + unsigned x = 0; + unsigned y = 0; + float dpiX = -1.0f; + float dpiY = -1.0f; + + // for debug purposes + static float scale = -2.0f; + if (scale == -2) { + scale = -1; + char *uiScale = getenv("J2D_UISCALE"); + if (uiScale != NULL) { + scale = (float)strtod(uiScale, NULL); + if (errno == ERANGE || scale <= 0) { + scale = -1; + } + } + } + + if (scale > 0) { + SetScale(scale, scale); + return; + } + + typedef HRESULT(WINAPI GetDpiForMonitorFunc)(HMONITOR, int, UINT*, UINT*); + static HMODULE hLibSHCoreDll = NULL; + static GetDpiForMonitorFunc *lpGetDpiForMonitor = NULL; + + if (hLibSHCoreDll == NULL) { + hLibSHCoreDll = JDK_LoadSystemLibrary("shcore.dll"); + if (hLibSHCoreDll != NULL) { + lpGetDpiForMonitor = (GetDpiForMonitorFunc*)GetProcAddress( + hLibSHCoreDll, "GetDpiForMonitor"); + } + } + + if (lpGetDpiForMonitor != NULL) { + HRESULT hResult = lpGetDpiForMonitor(GetMonitor(), + MDT_Effective_DPI, &x, &y); + if (hResult == S_OK) { + dpiX = static_cast(x); + dpiY = static_cast(y); + } + } else { + ID2D1Factory* m_pDirect2dFactory; + HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + &m_pDirect2dFactory); + if (res == S_OK) { + m_pDirect2dFactory->GetDesktopDpi(&dpiX, &dpiY); + m_pDirect2dFactory->Release(); + } + } + + if (dpiX > 0 && dpiY > 0) { + SetScale(dpiX / 96, dpiY / 96); + } +} + +float AwtWin32GraphicsDevice::GetScaleX() +{ + return scaleX; +} + +float AwtWin32GraphicsDevice::GetScaleY() +{ + return scaleY; +} + /** * Disables offscreen acceleration for this device. This * sets a flag in the java object that is used to determine @@ -1304,3 +1410,65 @@ JNIEXPORT void JNICALL Devices::InstanceAccess devices; devices->GetDevice(screen)->SetJavaDevice(env, thisPtr); } + +/* + * Class: sun_awt_Win32GraphicsDevice + * Method: setNativeScale + * Signature: (I,F,F)V + */ +JNIEXPORT void JNICALL + Java_sun_awt_Win32GraphicsDevice_setNativeScale + (JNIEnv *env, jobject thisPtr, jint screen, jfloat scaleX, jfloat scaleY) +{ + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(screen); + + if (device != NULL ) { + device->SetScale(scaleX, scaleY); + } +} + +/* + * Class: sun_awt_Win32GraphicsDevice + * Method: getNativeScaleX + * Signature: (I)F + */ +JNIEXPORT jfloat JNICALL + Java_sun_awt_Win32GraphicsDevice_getNativeScaleX + (JNIEnv *env, jobject thisPtr, jint screen) +{ + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(screen); + return (device == NULL) ? 1 : device->GetScaleX(); +} + +/* + * Class: sun_awt_Win32GraphicsDevice + * Method: getNativeScaleY + * Signature: (I)F + */ +JNIEXPORT jfloat JNICALL + Java_sun_awt_Win32GraphicsDevice_getNativeScaleY + (JNIEnv *env, jobject thisPtr, jint screen) +{ + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(screen); + return (device == NULL) ? 1 : device->GetScaleY(); +} + +/* +* Class: sun_awt_Win32GraphicsDevice +* Method: initNativeScale +* Signature: (I)V; +*/ +JNIEXPORT void JNICALL +Java_sun_awt_Win32GraphicsDevice_initNativeScale +(JNIEnv *env, jobject thisPtr, jint screen) +{ + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice *device = devices->GetDevice(screen); + + if (device != NULL) { + device->InitDesktopScales(); + } +} \ No newline at end of file diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h index e09b4fc794c..250df7af727 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h @@ -66,6 +66,14 @@ public: void Release(); void DisableOffscreenAcceleration(); void Invalidate(JNIEnv *env); + void InitDesktopScales(); + void SetScale(float scaleX, float scaleY); + float GetScaleX(); + float GetScaleY(); + int ScaleUpX(int x); + int ScaleUpY(int y); + int ScaleDownX(int x); + int ScaleDownY(int y); static int DeviceIndexForWindow(HWND hWnd); static jobject GetColorModel(JNIEnv *env, jboolean dynamic, @@ -107,6 +115,8 @@ private: LPMONITORINFO pMonitorInfo; jobject javaDevice; Devices *devicesArray; + float scaleX; + float scaleY; static HDC MakeDCFromMonitor(HMONITOR); }; diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp index 4a3bcfcad7d..096bf4f9f14 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp @@ -1407,19 +1407,19 @@ BOOL AwtWindow::UpdateInsets(jobject insets) /* Get insets into our peer directly */ jobject peerInsets = (env)->GetObjectField(peer, AwtPanel::insets_ID); DASSERT(!safe_ExceptionOccurred(env)); + if (peerInsets != NULL) { // may have been called during creation - (env)->SetIntField(peerInsets, AwtInsets::topID, m_insets.top); - (env)->SetIntField(peerInsets, AwtInsets::bottomID, - m_insets.bottom); - (env)->SetIntField(peerInsets, AwtInsets::leftID, m_insets.left); - (env)->SetIntField(peerInsets, AwtInsets::rightID, m_insets.right); + (env)->SetIntField(peerInsets, AwtInsets::topID, ScaleDownY(m_insets.top)); + (env)->SetIntField(peerInsets, AwtInsets::bottomID, ScaleDownY(m_insets.bottom)); + (env)->SetIntField(peerInsets, AwtInsets::leftID, ScaleDownX(m_insets.left)); + (env)->SetIntField(peerInsets, AwtInsets::rightID, ScaleDownX(m_insets.right)); } /* Get insets into the Inset object (if any) that was passed */ if (insets != NULL) { - (env)->SetIntField(insets, AwtInsets::topID, m_insets.top); - (env)->SetIntField(insets, AwtInsets::bottomID, m_insets.bottom); - (env)->SetIntField(insets, AwtInsets::leftID, m_insets.left); - (env)->SetIntField(insets, AwtInsets::rightID, m_insets.right); + (env)->SetIntField(insets, AwtInsets::topID, ScaleDownY(m_insets.top)); + (env)->SetIntField(insets, AwtInsets::bottomID, ScaleDownY(m_insets.bottom)); + (env)->SetIntField(insets, AwtInsets::leftID, ScaleDownX(m_insets.left)); + (env)->SetIntField(insets, AwtInsets::rightID, ScaleDownX(m_insets.right)); } env->DeleteLocalRef(peerInsets); @@ -1735,10 +1735,10 @@ MsgRouting AwtWindow::WmMove(int x, int y) RECT rect; ::GetWindowRect(GetHWnd(), &rect); - (env)->SetIntField(target, AwtComponent::xID, rect.left); - (env)->SetIntField(target, AwtComponent::yID, rect.top); - (env)->SetIntField(peer, AwtWindow::sysXID, rect.left); - (env)->SetIntField(peer, AwtWindow::sysYID, rect.top); + (env)->SetIntField(target, AwtComponent::xID, ScaleDownX(rect.left)); + (env)->SetIntField(target, AwtComponent::yID, ScaleDownY(rect.top)); + (env)->SetIntField(peer, AwtWindow::sysXID, ScaleDownX(rect.left)); + (env)->SetIntField(peer, AwtWindow::sysYID, ScaleDownY(rect.top)); SendComponentEvent(java_awt_event_ComponentEvent_COMPONENT_MOVED); env->DeleteLocalRef(target); @@ -1803,12 +1803,12 @@ MsgRouting AwtWindow::WmSize(UINT type, int w, int h) int newWidth = w + m_insets.left + m_insets.right; int newHeight = h + m_insets.top + m_insets.bottom; - (env)->SetIntField(target, AwtComponent::widthID, newWidth); - (env)->SetIntField(target, AwtComponent::heightID, newHeight); + (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(newWidth)); + (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(newHeight)); jobject peer = GetPeer(env); - (env)->SetIntField(peer, AwtWindow::sysWID, newWidth); - (env)->SetIntField(peer, AwtWindow::sysHID, newHeight); + (env)->SetIntField(peer, AwtWindow::sysWID, ScaleDownX(newWidth)); + (env)->SetIntField(peer, AwtWindow::sysHID, ScaleDownY(newHeight)); if (!AwtWindow::IsResizing()) { WindowResized(); @@ -3072,6 +3072,25 @@ void AwtWindow::_SetFullScreenExclusiveModeState(void *param) delete data; } +void AwtWindow::_GetNativeWindowSize(void* param) { + + JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); + + SizeStruct *ss = (SizeStruct *)param; + jobject self = ss->window; + AwtWindow *window = NULL; + PDATA pData; + JNI_CHECK_PEER_RETURN(self); + window = (AwtWindow *)pData; + + RECT rc; + ::GetWindowRect(window->GetHWnd(), &rc); + ss->w = rc.right - rc.left; + ss->h = rc.bottom - rc.top; + + env->DeleteGlobalRef(self); +} + extern "C" { /* @@ -3301,6 +3320,46 @@ Java_sun_awt_windows_WWindowPeer_reshapeFrame(JNIEnv *env, jobject self, CATCH_BAD_ALLOC; } +/* + * Class: sun_awt_windows_WWindowPeer +* Method: getNativeWindowSize +* Signature: ()Ljava/awt/Dimension; +*/ +JNIEXPORT jobject JNICALL Java_sun_awt_windows_WWindowPeer_getNativeWindowSize +(JNIEnv *env, jobject self) { + + jobject res = NULL; + TRY; + SizeStruct *ss = new SizeStruct; + ss->window = env->NewGlobalRef(self); + + AwtToolkit::GetInstance().SyncCall(AwtWindow::_GetNativeWindowSize, ss); + + int w = ss->w; + int h = ss->h; + + delete ss; + // global ref is deleted in _GetNativeWindowSize() + + static jmethodID dimMID = NULL; + static jclass dimClassID = NULL; + if (dimClassID == NULL) { + jclass dimClassIDLocal = env->FindClass("java/awt/Dimension"); + CHECK_NULL_RETURN(dimClassIDLocal, NULL); + dimClassID = (jclass)env->NewGlobalRef(dimClassIDLocal); + env->DeleteLocalRef(dimClassIDLocal); + } + + if (dimMID == NULL) { + dimMID = env->GetMethodID(dimClassID, "", "(II)V"); + CHECK_NULL_RETURN(dimMID, NULL); + } + + return env->NewObject(dimClassID, dimMID, w, h); + + CATCH_BAD_ALLOC_RET(NULL); +} + /* * Class: sun_awt_windows_WWindowPeer * Method: getSysMinWidth diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.h b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.h index 1a92a672c9f..c1ce65be4e4 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.h +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.h @@ -241,6 +241,7 @@ public: static void _UpdateWindow(void* param); static void _RepositionSecurityWarning(void* param); static void _SetFullScreenExclusiveModeState(void* param); + static void _GetNativeWindowSize(void* param); inline static BOOL IsResizing() { return sm_resizing; diff --git a/jdk/test/java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java b/jdk/test/java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java new file mode 100644 index 00000000000..b551b155450 --- /dev/null +++ b/jdk/test/java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java @@ -0,0 +1,87 @@ +/* + * 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.Frame; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.UIManager; + +/* @test + * @bug 8073320 + * @summary Windows HiDPI support + * @author Alexander Scherbatiy + * @requires (os.family == "windows") + * @run main/othervm -Dsun.java2d.win.uiScale=2 HiDPIRobotMouseClick + */ +public class HiDPIRobotMouseClick { + + private static volatile int mouseX; + private static volatile int mouseY; + + public static void main(String[] args) throws Exception { + + try { + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + } catch (Exception e) { + return; + } + + Frame frame = new Frame(); + frame.setBounds(30, 20, 400, 300); + frame.setUndecorated(true); + + frame.addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + mouseX = e.getXOnScreen(); + mouseY = e.getYOnScreen(); + } + }); + + frame.setVisible(true); + + Robot robot = new Robot(); + robot.waitForIdle(); + Thread.sleep(200); + + Rectangle rect = frame.getBounds(); + rect.setLocation(frame.getLocationOnScreen()); + + int x = (int) rect.getCenterX(); + int y = (int) rect.getCenterY(); + + robot.mouseMove(x, y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.waitForIdle(); + + if (x != mouseX || y != mouseY) { + throw new RuntimeException("Wrong mouse click point!"); + } + } +} diff --git a/jdk/test/java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java b/jdk/test/java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java new file mode 100644 index 00000000000..03a346abff1 --- /dev/null +++ b/jdk/test/java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java @@ -0,0 +1,115 @@ +/* + * 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.Canvas; +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import javax.swing.UIManager; + +/* @test + * @bug 8073320 + * @summary Windows HiDPI support + * @author Alexander Scherbatiy + * @requires (os.family == "windows") + * @run main/othervm -Dsun.java2d.win.uiScaleX=3 -Dsun.java2d.win.uiScaleY=2 + * HiDPIRobotScreenCaptureTest + */ +public class HiDPIRobotScreenCaptureTest { + + private static final Color[] COLORS = { + Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED}; + + public static void main(String[] args) throws Exception { + + try { + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + } catch (Exception e) { + return; + } + + Frame frame = new Frame(); + frame.setBounds(40, 30, 400, 300); + frame.setUndecorated(true); + + Panel panel = new Panel(new BorderLayout()); + Canvas canvas = new Canvas() { + @Override + public void paint(Graphics g) { + super.paint(g); + int w = getWidth(); + int h = getHeight(); + g.setColor(COLORS[0]); + g.fillRect(0, 0, w / 2, h / 2); + g.setColor(COLORS[1]); + g.fillRect(w / 2, 0, w / 2, h / 2); + g.setColor(COLORS[2]); + g.fillRect(0, h / 2, w / 2, h / 2); + g.setColor(COLORS[3]); + g.fillRect(w / 2, h / 2, w / 2, h / 2); + } + }; + + panel.add(canvas); + frame.add(panel); + frame.setVisible(true); + Robot robot = new Robot(); + robot.waitForIdle(); + Thread.sleep(200); + + Rectangle rect = canvas.getBounds(); + rect.setLocation(canvas.getLocationOnScreen()); + + BufferedImage image = robot.createScreenCapture(rect); + frame.dispose(); + + int w = image.getWidth(); + int h = image.getHeight(); + + if (w != frame.getWidth() || h != frame.getHeight()) { + throw new RuntimeException("Wrong image size!"); + } + + if (image.getRGB(w / 4, h / 4) != COLORS[0].getRGB()) { + throw new RuntimeException("Wrong image color!"); + } + + if (image.getRGB(3 * w / 4, h / 4) != COLORS[1].getRGB()) { + throw new RuntimeException("Wrong image color!"); + } + + if (image.getRGB(w / 4, 3 * h / 4) != COLORS[2].getRGB()) { + throw new RuntimeException("Wrong image color!"); + } + + if (image.getRGB(3 * w / 4, 3 * h / 4) != COLORS[3].getRGB()) { + throw new RuntimeException("Wrong image color!"); + } + } +} \ No newline at end of file diff --git a/jdk/test/java/awt/hidpi/properties/HiDPIPropertiesWindowsTest.java b/jdk/test/java/awt/hidpi/properties/HiDPIPropertiesWindowsTest.java new file mode 100644 index 00000000000..56aa9a3b92c --- /dev/null +++ b/jdk/test/java/awt/hidpi/properties/HiDPIPropertiesWindowsTest.java @@ -0,0 +1,139 @@ +/* + * 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.Dialog; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import javax.swing.UIManager; + +/* @test + * @bug 8073320 + * @summary Windows HiDPI support + * @author Alexander Scherbatiy + * @requires (os.family == "windows") + * @run main/othervm -Dsun.java2d.uiScale.enabled=false + * -Dsun.java2d.win.uiScaleX=3 -Dsun.java2d.win.uiScaleY=2 + * HiDPIPropertiesWindowsTest UISCALE_DISABLED + * @run main/othervm -Dsun.java2d.uiScale.enabled=false + * -Dsun.java2d.uiScale=3 + * HiDPIPropertiesWindowsTest UISCALE_DISABLED + * @run main/othervm -Dsun.java2d.uiScale.enabled=false + * -Dsun.java2d.uiScale=3 + * -Dsun.java2d.win.uiScaleX=5 -Dsun.java2d.win.uiScaleY=6 + * HiDPIPropertiesWindowsTest UISCALE_DISABLED + * @run main/othervm -Dsun.java2d.uiScale.enabled=true + * -Dsun.java2d.uiScale=3 + * HiDPIPropertiesWindowsTest UISCALE_3 + * @run main/othervm -Dsun.java2d.uiScale.enabled=true + * -Dsun.java2d.uiScale=4 + * -Dsun.java2d.win.uiScaleX=2 -Dsun.java2d.win.uiScaleY=3 + * HiDPIPropertiesWindowsTest UISCALE_2X3 + * @run main/othervm -Dsun.java2d.uiScale.enabled=true + * -Dsun.java2d.win.uiScaleX=3 -Dsun.java2d.win.uiScaleY=2 + * HiDPIPropertiesWindowsTest UISCALE_3X2 + * @run main/othervm -Dsun.java2d.uiScale=4 + * HiDPIPropertiesWindowsTest UISCALE_4 + * @run main/othervm -Dsun.java2d.uiScale=4 + * -Dsun.java2d.win.uiScaleX=2 -Dsun.java2d.win.uiScaleY=3 + * HiDPIPropertiesWindowsTest UISCALE_2X3 + * @run main/othervm -Dsun.java2d.win.uiScaleX=4 -Dsun.java2d.win.uiScaleY=5 + * HiDPIPropertiesWindowsTest UISCALE_4X5 + * @run main/othervm -Dsun.java2d.uiScale=3 + * -Dsun.java2d.win.uiScaleX=0 -Dsun.java2d.win.uiScaleY=0 + * HiDPIPropertiesWindowsTest UISCALE_3 + * @run main/othervm -Dsun.java2d.uiScale=4 + * -Dsun.java2d.win.uiScaleX=-7 -Dsun.java2d.win.uiScaleY=-8 + * HiDPIPropertiesWindowsTest UISCALE_4 + * @run main/othervm -Dsun.java2d.uiScale=4x + * HiDPIPropertiesWindowsTest UISCALE_4 + * @run main/othervm -Dsun.java2d.win.uiScaleX=4x -Dsun.java2d.win.uiScaleY=5x + * HiDPIPropertiesWindowsTest UISCALE_4X5 + * @run main/othervm -Dsun.java2d.uiScale=384dpi + * HiDPIPropertiesWindowsTest UISCALE_4 + * @run main/othervm -Dsun.java2d.uiScale=300% + * HiDPIPropertiesWindowsTest UISCALE_3 + * @run main/othervm -Dsun.java2d.win.uiScaleX=400% -Dsun.java2d.win.uiScaleY=500% + * HiDPIPropertiesWindowsTest UISCALE_4X5 + * @run main/othervm -Dsun.java2d.win.uiScaleX=288dpi -Dsun.java2d.win.uiScaleY=192dpi + * HiDPIPropertiesWindowsTest UISCALE_3X2 + * @run main/othervm -Dsun.java2d.win.uiScaleX=200% -Dsun.java2d.win.uiScaleY=288dpi + * HiDPIPropertiesWindowsTest UISCALE_2X3 + */ +public class HiDPIPropertiesWindowsTest { + + public static void main(String[] args) throws Exception { + + try { + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + } catch (Exception e) { + return; + } + + String testCase = args[0]; + switch (testCase) { + case "UISCALE_DISABLED": + testScale(1.0, 1.0); + break; + case "UISCALE_3": + testScale(3.0, 3.0); + break; + case "UISCALE_4": + testScale(4.0, 4.0); + break; + case "UISCALE_2X3": + testScale(2.0, 3.0); + break; + case "UISCALE_3X2": + testScale(3.0, 2.0); + break; + case "UISCALE_4X5": + testScale(4.0, 5.0); + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + } + + private static void testScale(double scaleX, double scaleY) { + + Dialog dialog = new Dialog((Frame) null, true) { + + @Override + public void paint(Graphics g) { + super.paint(g); + AffineTransform tx = ((Graphics2D) g).getTransform(); + dispose(); + if (scaleX != tx.getScaleX() || scaleY != tx.getScaleY()) { + throw new RuntimeException(String.format("Wrong scale:" + + "[%f, %f] instead of [%f, %f].", + tx.getScaleX(), tx.getScaleY(), scaleX, scaleY)); + } + } + }; + dialog.setSize(200, 300); + dialog.setVisible(true); + } +} diff --git a/jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java b/jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java new file mode 100644 index 00000000000..aa4a932d281 --- /dev/null +++ b/jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java @@ -0,0 +1,248 @@ +/* + * 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.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.BaseMultiResolutionImage; +import static java.awt.RenderingHints.KEY_RESOLUTION_VARIANT; +import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_SIZE_FIT; +import java.awt.geom.AffineTransform; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import sun.java2d.StateTrackable; +import sun.java2d.SunGraphics2D; +import sun.java2d.SurfaceData; +import sun.java2d.loops.SurfaceType; + +/** + * @test + * @bug 8073320 + * @author Alexander Scherbatiy + * @summary Windows HiDPI support + * @modules java.desktop/sun.java2d java.desktop/sun.java2d.loops + * @run main MultiResolutionDrawImageWithTransformTest + */ +public class MultiResolutionDrawImageWithTransformTest { + + private static final int SCREEN_SIZE = 400; + private static final int IMAGE_SIZE = SCREEN_SIZE / 4; + private static final Color BACKGROUND_COLOR = Color.PINK; + private static final Color[] COLORS = { + Color.CYAN, Color.GREEN, Color.BLUE, Color.ORANGE + }; + + public static void main(String[] args) throws Exception { + + int length = COLORS.length; + BufferedImage[] resolutionVariants = new BufferedImage[length]; + for (int i = 0; i < length; i++) { + resolutionVariants[i] = createRVImage(getSize(i), COLORS[i]); + } + + BaseMultiResolutionImage mrImage = new BaseMultiResolutionImage( + resolutionVariants); + + // scale 1, transform 1, resolution variant 1 + Color color = getImageColor(mrImage, 1, 1); + if (!getColorForScale(1).equals(color)) { + throw new RuntimeException("Wrong resolution variant!"); + } + + // scale 1, transform 2, resolution variant 2 + color = getImageColor(mrImage, 1, 2); + if (!getColorForScale(2).equals(color)) { + throw new RuntimeException("Wrong resolution variant!"); + } + + // scale 2, transform 1, resolution variant 2 + color = getImageColor(mrImage, 2, 1); + if (!getColorForScale(2).equals(color)) { + throw new RuntimeException("Wrong resolution variant!"); + } + + // scale 2, transform 2, resolution variant 4 + color = getImageColor(mrImage, 2, 2); + if (!getColorForScale(4).equals(color)) { + throw new RuntimeException("Wrong resolution variant!"); + } + } + + private static Color getColorForScale(int scale) { + return COLORS[scale - 1]; + } + + private static Color getImageColor(Image image, double configScale, + double transformScale) { + + TestSurfaceData surface = new TestSurfaceData(SCREEN_SIZE, SCREEN_SIZE, + configScale); + SunGraphics2D g2d = new SunGraphics2D(surface, + Color.BLACK, Color.BLACK, null); + g2d.setRenderingHint(KEY_RESOLUTION_VARIANT, + VALUE_RESOLUTION_VARIANT_SIZE_FIT); + AffineTransform tx = AffineTransform.getScaleInstance(transformScale, + transformScale); + g2d.drawImage(image, tx, null); + g2d.dispose(); + + int backgroundX = (int) (1.5 * image.getWidth(null) * transformScale); + int backgroundY = (int) (1.5 * image.getHeight(null) * transformScale); + Color backgroundColor = surface.getColor(backgroundX, backgroundY); + //surface.show(String.format("Config: %f, transform: %f", configScale, transformScale)); + if (!BACKGROUND_COLOR.equals(backgroundColor)) { + throw new RuntimeException("Wrong background color!"); + } + return surface.getColor(IMAGE_SIZE / 4, IMAGE_SIZE / 4); + } + + private static int getSize(int i) { + return (i + 1) * IMAGE_SIZE; + } + + private static BufferedImage createRVImage(int size, Color color) { + BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB); + Graphics g = image.createGraphics(); + g.setColor(color); + g.fillRect(0, 0, size, size); + g.dispose(); + return image; + } + + static class TestGraphicsConfig extends GraphicsConfiguration { + + private final double scale; + + TestGraphicsConfig(double scale) { + this.scale = scale; + } + + @Override + public GraphicsDevice getDevice() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ColorModel getColorModel() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ColorModel getColorModel(int transparency) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public AffineTransform getDefaultTransform() { + return AffineTransform.getScaleInstance(scale, scale); + } + + @Override + public AffineTransform getNormalizingTransform() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Rectangle getBounds() { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + static class TestSurfaceData extends SurfaceData { + + private final int width; + private final int height; + private final GraphicsConfiguration gc; + private final BufferedImage buffImage; + private final double scale; + + public TestSurfaceData(int width, int height, double scale) { + super(StateTrackable.State.DYNAMIC, SurfaceType.Custom, ColorModel.getRGBdefault()); + this.scale = scale; + gc = new TestGraphicsConfig(scale); + this.width = (int) Math.ceil(scale * width); + this.height = (int) Math.ceil(scale * height); + buffImage = new BufferedImage(this.width, this.height, + BufferedImage.TYPE_INT_RGB); + + Graphics imageGraphics = buffImage.createGraphics(); + imageGraphics.setColor(BACKGROUND_COLOR); + imageGraphics.fillRect(0, 0, this.width, this.height); + imageGraphics.dispose(); + } + + Color getColor(int x, int y) { + int sx = (int) Math.ceil(x * scale); + int sy = (int) Math.ceil(y * scale); + return new Color(buffImage.getRGB(sx, sy)); + } + + @Override + public SurfaceData getReplacement() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public GraphicsConfiguration getDeviceConfiguration() { + return gc; + } + + @Override + public Raster getRaster(int x, int y, int w, int h) { + return buffImage.getRaster(); + } + + @Override + public Rectangle getBounds() { + return new Rectangle(0, 0, width, height); + } + + @Override + public Object getDestination() { + throw new UnsupportedOperationException("Not supported yet."); + } + + private void show(String title) { + Frame frame = new Frame() { + + @Override + public void paint(Graphics g) { + super.paint(g); + g.drawImage(buffImage, 0, 0, this); + g.setColor(Color.GRAY); + g.drawRect(0, 0, width, height); + g.drawRect(0, height / 2, width, height / 2); + g.drawRect(width / 2, 0, width / 2, height); + } + }; + frame.setTitle(title); + frame.setSize(width, height); + frame.setVisible(true); + } + } +} From 11e31407a5b9024a07e5aa6d83e62ef9f3059a15 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Sun, 29 Nov 2015 20:27:38 +0000 Subject: [PATCH 006/199] 8144210: Runtime.currentRuntime should be final Reviewed-by: dl, dholmes --- jdk/src/java.base/share/classes/java/lang/Runtime.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/java.base/share/classes/java/lang/Runtime.java b/jdk/src/java.base/share/classes/java/lang/Runtime.java index e75fcb439cf..668357e7067 100644 --- a/jdk/src/java.base/share/classes/java/lang/Runtime.java +++ b/jdk/src/java.base/share/classes/java/lang/Runtime.java @@ -44,7 +44,7 @@ import sun.reflect.Reflection; */ public class Runtime { - private static Runtime currentRuntime = new Runtime(); + private static final Runtime currentRuntime = new Runtime(); /** * Returns the runtime object associated with the current Java application. From 5bb97261178371daad555233e7b3cb57b7ca7b01 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Fri, 13 Nov 2015 14:44:05 +0100 Subject: [PATCH 007/199] 8043138: Attach API should not require jvmstat rmi protocol Reviewed-by: alanb, mchung, erikj, ihse --- jdk/make/gensrc/Gensrc-jdk.jvmstat.gmk | 55 +++++++++++++++++++ ...mstat.gmk => Launcher-jdk.jvmstat.rmi.gmk} | 0 .../sun.jvmstat.monitor.MonitoredHostService | 1 + .../jvmstat/monitor/remote/RemoteHost.java | 0 .../sun/jvmstat/monitor/remote/RemoteVm.java | 0 .../sun/jvmstat/monitor/remote/package.html | 0 .../protocol/rmi/MonitoredHostProvider.java | 0 .../protocol/rmi/MonitoredHostRmiService.java | 0 .../monitor/protocol/rmi/PerfDataBuffer.java | 0 .../protocol/rmi/RemoteMonitoredVm.java | 0 .../monitor/protocol/rmi/RemoteVmManager.java | 0 .../monitor/protocol/rmi/package.html | 0 .../classes/sun/tools/jstatd/Jstatd.java | 0 .../sun/tools/jstatd/RemoteHostImpl.java | 0 .../sun/tools/jstatd/RemoteVmImpl.java | 0 .../sun.jvmstat.monitor.MonitoredHostService | 1 - .../perfdata/monitor/AbstractMonitoredVm.java | 3 - 17 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 jdk/make/gensrc/Gensrc-jdk.jvmstat.gmk rename jdk/make/launcher/{Launcher-jdk.jvmstat.gmk => Launcher-jdk.jvmstat.rmi.gmk} (100%) create mode 100644 jdk/src/jdk.jvmstat.rmi/share/classes/META-INF/services/sun.jvmstat.monitor.MonitoredHostService rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/monitor/remote/RemoteHost.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/monitor/remote/RemoteVm.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/monitor/remote/package.html (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostProvider.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostRmiService.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/PerfDataBuffer.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteMonitoredVm.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteVmManager.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/package.html (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/tools/jstatd/Jstatd.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/tools/jstatd/RemoteHostImpl.java (100%) rename jdk/src/{jdk.jvmstat => jdk.jvmstat.rmi}/share/classes/sun/tools/jstatd/RemoteVmImpl.java (100%) diff --git a/jdk/make/gensrc/Gensrc-jdk.jvmstat.gmk b/jdk/make/gensrc/Gensrc-jdk.jvmstat.gmk new file mode 100644 index 00000000000..11e54a6f635 --- /dev/null +++ b/jdk/make/gensrc/Gensrc-jdk.jvmstat.gmk @@ -0,0 +1,55 @@ +# +# 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. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# 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. +# + +include GensrcCommon.gmk + +################################################################################ + +define merge-providers + $(MKDIR) -p $(@D) + $(CAT) $^ > $@ +endef + +PROVIDER_FILE := META-INF/services/sun.jvmstat.monitor.MonitoredHostService + +# Merge the local and remote sevice providers into jdk.jvmstat/META-INF/services +$(SUPPORT_OUTPUTDIR)/gensrc/jdk.jvmstat/$(PROVIDER_FILE): \ + $(JDK_TOPDIR)/src/jdk.jvmstat/share/classes/$(PROVIDER_FILE) \ + $(JDK_TOPDIR)/src/jdk.jvmstat.rmi/share/classes/$(PROVIDER_FILE) + $(merge-providers) + +# Copy the same service file into jdk.jvmstat.rmi so that they are kept the same. +$(SUPPORT_OUTPUTDIR)/gensrc/jdk.jvmstat.rmi/$(PROVIDER_FILE): \ + $(SUPPORT_OUTPUTDIR)/gensrc/jdk.jvmstat/$(PROVIDER_FILE) + $(install-file) + +################################################################################ + +jdk.jvmstat: $(SUPPORT_OUTPUTDIR)/gensrc/jdk.jvmstat/$(PROVIDER_FILE) \ + $(SUPPORT_OUTPUTDIR)/gensrc/jdk.jvmstat.rmi/$(PROVIDER_FILE) + +all: jdk.jvmstat + +.PHONY: all \ No newline at end of file diff --git a/jdk/make/launcher/Launcher-jdk.jvmstat.gmk b/jdk/make/launcher/Launcher-jdk.jvmstat.rmi.gmk similarity index 100% rename from jdk/make/launcher/Launcher-jdk.jvmstat.gmk rename to jdk/make/launcher/Launcher-jdk.jvmstat.rmi.gmk diff --git a/jdk/src/jdk.jvmstat.rmi/share/classes/META-INF/services/sun.jvmstat.monitor.MonitoredHostService b/jdk/src/jdk.jvmstat.rmi/share/classes/META-INF/services/sun.jvmstat.monitor.MonitoredHostService new file mode 100644 index 00000000000..9ae6583ef3e --- /dev/null +++ b/jdk/src/jdk.jvmstat.rmi/share/classes/META-INF/services/sun.jvmstat.monitor.MonitoredHostService @@ -0,0 +1 @@ +sun.jvmstat.perfdata.monitor.protocol.rmi.MonitoredHostRmiService diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/remote/RemoteHost.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/monitor/remote/RemoteHost.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/remote/RemoteHost.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/monitor/remote/RemoteHost.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/remote/RemoteVm.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/monitor/remote/RemoteVm.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/remote/RemoteVm.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/monitor/remote/RemoteVm.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/remote/package.html b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/monitor/remote/package.html similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/remote/package.html rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/monitor/remote/package.html diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostProvider.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostProvider.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostProvider.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostProvider.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostRmiService.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostRmiService.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostRmiService.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/MonitoredHostRmiService.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/PerfDataBuffer.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/PerfDataBuffer.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/PerfDataBuffer.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/PerfDataBuffer.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteMonitoredVm.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteMonitoredVm.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteMonitoredVm.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteMonitoredVm.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteVmManager.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteVmManager.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteVmManager.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/RemoteVmManager.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/package.html b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/package.html similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/package.html rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/jvmstat/perfdata/monitor/protocol/rmi/package.html diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/tools/jstatd/Jstatd.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/tools/jstatd/Jstatd.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/tools/jstatd/Jstatd.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/tools/jstatd/Jstatd.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/tools/jstatd/RemoteHostImpl.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/tools/jstatd/RemoteHostImpl.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/tools/jstatd/RemoteHostImpl.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/tools/jstatd/RemoteHostImpl.java diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/tools/jstatd/RemoteVmImpl.java b/jdk/src/jdk.jvmstat.rmi/share/classes/sun/tools/jstatd/RemoteVmImpl.java similarity index 100% rename from jdk/src/jdk.jvmstat/share/classes/sun/tools/jstatd/RemoteVmImpl.java rename to jdk/src/jdk.jvmstat.rmi/share/classes/sun/tools/jstatd/RemoteVmImpl.java diff --git a/jdk/src/jdk.jvmstat/share/classes/META-INF/services/sun.jvmstat.monitor.MonitoredHostService b/jdk/src/jdk.jvmstat/share/classes/META-INF/services/sun.jvmstat.monitor.MonitoredHostService index b106c0fc9a9..f559e14c4b4 100644 --- a/jdk/src/jdk.jvmstat/share/classes/META-INF/services/sun.jvmstat.monitor.MonitoredHostService +++ b/jdk/src/jdk.jvmstat/share/classes/META-INF/services/sun.jvmstat.monitor.MonitoredHostService @@ -1,3 +1,2 @@ sun.jvmstat.perfdata.monitor.protocol.file.MonitoredHostFileService sun.jvmstat.perfdata.monitor.protocol.local.MonitoredHostLocalService -sun.jvmstat.perfdata.monitor.protocol.rmi.MonitoredHostRmiService diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/AbstractMonitoredVm.java b/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/AbstractMonitoredVm.java index 85365ce056c..8df13ca7f30 100644 --- a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/AbstractMonitoredVm.java +++ b/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/AbstractMonitoredVm.java @@ -26,11 +26,8 @@ package sun.jvmstat.perfdata.monitor; import java.util.List; -import java.lang.reflect.*; -import java.io.*; import sun.jvmstat.monitor.*; -import sun.jvmstat.monitor.remote.*; import sun.jvmstat.monitor.event.VmListener; /** From 4dd2fac63ccd9776d5377022032f1ae82a4d1dc4 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 27 Nov 2015 09:37:33 +0100 Subject: [PATCH 008/199] 8142303: C2 compilation fails with "bad AD file" Move range checks into intrinsics for String methods. Reviewed-by: kvn, aph --- .../classes/java/lang/AbstractStringBuilder.java | 15 ++++++--------- .../share/classes/java/lang/String.java | 3 +-- .../share/classes/java/lang/StringBuilder.java | 2 +- .../share/classes/java/lang/StringLatin1.java | 16 +++------------- .../share/classes/java/lang/StringUTF16.java | 15 +++++---------- 5 files changed, 16 insertions(+), 35 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index 3cd12eeac15..ae1e28236ca 100644 --- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -177,7 +177,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { return; } byte[] buf = StringUTF16.newBytesFor(value.length); - StringLatin1.inflateSB(value, buf, 0, count); + StringLatin1.inflate(value, 0, buf, 0, count); this.value = buf; this.coder = UTF16; } @@ -414,9 +414,9 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { int n = srcEnd - srcBegin; checkRange(dstBegin, dstBegin + n, dst.length); if (isLatin1()) { - StringLatin1.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin); + StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin); } else { - StringUTF16.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin); + StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin); } } @@ -992,7 +992,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { if (isLatin1()) { return StringLatin1.newString(value, start, end - start); } - return StringUTF16.newStringSB(value, start, end - start); + return StringUTF16.newString(value, start, end - start); } private void shift(int offset, int n) { @@ -1588,7 +1588,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { if (this.coder == coder) { System.arraycopy(value, 0, dst, dstBegin << coder, count << coder); } else { // this.coder == LATIN && coder == UTF16 - StringLatin1.inflateSB(value, dst, dstBegin, count); + StringLatin1.inflate(value, 0, dst, dstBegin, count); } } @@ -1653,10 +1653,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { if (getCoder() != str.coder()) { inflate(); } - byte[] val = this.value; - byte coder = this.coder; - checkOffset(index + str.length(), val.length >> coder); - str.getBytes(val, index, coder); + str.getBytes(value, index, coder); } private final void appendChars(char[] s, int off, int end) { diff --git a/jdk/src/java.base/share/classes/java/lang/String.java b/jdk/src/java.base/share/classes/java/lang/String.java index 4d4ab358b9f..63946f2e301 100644 --- a/jdk/src/java.base/share/classes/java/lang/String.java +++ b/jdk/src/java.base/share/classes/java/lang/String.java @@ -1720,7 +1720,6 @@ public final class String */ static int indexOf(byte[] src, byte srcCoder, int srcCount, String tgtStr, int fromIndex) { - byte[] tgt = tgtStr.value; byte tgtCoder = tgtStr.coder(); int tgtCount = tgtStr.length(); @@ -3103,7 +3102,7 @@ public final class String * If {@code offset} is negative, {@code count} is negative, * or {@code offset} is greater than {@code length - count} */ - private static void checkBoundsOffCount(int offset, int count, int length) { + static void checkBoundsOffCount(int offset, int count, int length) { if (offset < 0 || count < 0 || offset > length - count) { throw new StringIndexOutOfBoundsException( "offset " + offset + ", count " + count + ", length " + length); diff --git a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java index 7d1e46a423f..7e5d3d04bbe 100644 --- a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java +++ b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java @@ -413,7 +413,7 @@ public final class StringBuilder public String toString() { // Create a copy, don't share the array return isLatin1() ? StringLatin1.newString(value, 0, count) - : StringUTF16.newStringSB(value, 0, count); + : StringUTF16.newString(value, 0, count); } /** diff --git a/jdk/src/java.base/share/classes/java/lang/StringLatin1.java b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java index eb8ddc65121..8e8016d833e 100644 --- a/jdk/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java @@ -36,6 +36,7 @@ import jdk.internal.HotSpotIntrinsicCandidate; import static java.lang.String.LATIN1; import static java.lang.String.UTF16; import static java.lang.String.checkOffset; +import static java.lang.String.checkBoundsOffCount; final class StringLatin1 { @@ -523,6 +524,8 @@ final class StringLatin1 { // inflatedCopy byte[] -> byte[] @HotSpotIntrinsicCandidate public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + // We need a range check here because 'putChar' has no checks + checkBoundsOffCount(dstOff, len, dst.length); for (int i = 0; i < len; i++) { StringUTF16.putChar(dst, dstOff++, src[srcOff++] & 0xff); } @@ -584,17 +587,4 @@ final class StringLatin1 { return cs; } } - - //////////////////////////////////////////////////////////////// - - public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) { - checkOffset(srcEnd, val.length); - getChars(val, srcBegin, srcEnd, dst, dstBegin); - } - - public static void inflateSB(byte[] val, byte[] dst, int dstOff, int count) { - checkOffset(count, val.length); - checkOffset(dstOff + count, dst.length >> 1); // dst is utf16 - inflate(val, 0, dst, dstOff, count); - } } diff --git a/jdk/src/java.base/share/classes/java/lang/StringUTF16.java b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java index 56550d77fc6..c00d3ce0953 100644 --- a/jdk/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java @@ -35,6 +35,7 @@ import static java.lang.String.UTF16; import static java.lang.String.LATIN1; import static java.lang.String.checkIndex; import static java.lang.String.checkOffset; +import static java.lang.String.checkBoundsOffCount; final class StringUTF16 { @@ -156,6 +157,8 @@ final class StringUTF16 { // compressedCopy byte[] -> byte[] @HotSpotIntrinsicCandidate public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + // We need a range check here because 'getChar' has no checks + checkBoundsOffCount(srcOff, len, src.length); for (int i = 0; i < len; i++) { int c = getChar(src, srcOff++); if (c >>> 8 != 0) { @@ -200,6 +203,8 @@ final class StringUTF16 { @HotSpotIntrinsicCandidate public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) { + // We need a range check here because 'getChar' has no checks + checkBoundsOffCount(srcBegin, srcEnd - srcBegin, value.length); for (int i = srcBegin; i < srcEnd; i++) { dst[dstBegin++] = getChar(value, i); } @@ -909,11 +914,6 @@ final class StringUTF16 { //////////////////////////////////////////////////////////////// - public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) { - checkOffset(srcEnd, val.length >> 1); - getChars(val, srcBegin, srcEnd, dst, dstBegin); - } - public static void putCharSB(byte[] val, int index, int c) { checkIndex(index, val.length >> 1); putChar(val, index, c); @@ -946,11 +946,6 @@ final class StringUTF16 { return codePointCount(val, beginIndex, endIndex); } - public static String newStringSB(byte[] val, int index, int len) { - checkOffset(index + len, val.length >> 1); - return newString(val, index, len); - } - //////////////////////////////////////////////////////////////// private static native boolean isBigEndian(); From 4faa0ee15c1a16d6cbe6cef90c89640013510050 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 7 Dec 2015 14:12:58 -0800 Subject: [PATCH 009/199] 8144880: Instrument intermittently failing test ConfigChanges.java Reviewed-by: lancea, martin --- jdk/test/TEST.groups | 2 ++ .../util/concurrent/ThreadPoolExecutor/ConfigChanges.java | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/jdk/test/TEST.groups b/jdk/test/TEST.groups index 0600a4c8345..769d3f91e30 100644 --- a/jdk/test/TEST.groups +++ b/jdk/test/TEST.groups @@ -32,6 +32,7 @@ tier1 = \ :jdk_util \ -java/util/WeakHashMap/GCDuringIteration.java \ -java/util/concurrent/Phaser/Basic.java \ + -java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java sun/nio/cs/ISO8859x.java \ java/nio/Buffer \ com/sun/crypto/provider/Cipher \ @@ -42,6 +43,7 @@ tier2 = \ java/util/zip/TestLocalTime.java \ java/util/concurrent/Phaser/Basic.java \ java/util/WeakHashMap/GCDuringIteration.java \ + java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java \ :jdk_io \ :jdk_nio \ -sun/nio/cs/ISO8859x.java \ diff --git a/jdk/test/java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java b/jdk/test/java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java index 2418acc18c5..a7ff015bc77 100644 --- a/jdk/test/java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java +++ b/jdk/test/java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -25,7 +25,10 @@ * @test * @bug 6450200 * @summary Test proper handling of pool state changes + * @library /lib/testlibrary/ + * @build jdk.testlibrary.RandomFactory * @run main/othervm ConfigChanges + * @key randomness intermittent * @author Martin Buchholz */ @@ -42,11 +45,12 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; +import jdk.testlibrary.RandomFactory; public class ConfigChanges { static final ThreadGroup tg = new ThreadGroup("pool"); - static final Random rnd = new Random(); + static final Random rnd = RandomFactory.getRandom(); static void report(ThreadPoolExecutor tpe) { try { From 307b9775e8e3ec16f085d3f37466584aa43780e9 Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Tue, 8 Dec 2015 03:49:12 +0000 Subject: [PATCH 010/199] 8144890: Add the intermittent keyword test B6216082.java Reviewed-by: mullan --- .../protocol/https/HttpsURLConnection/B6216082.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java b/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java index 33af848f466..72bae7e4ed5 100644 --- a/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java +++ b/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -21,15 +21,20 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test * @bug 6216082 * @summary Redirect problem with HttpsURLConnection using a proxy - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. * @modules java.base/sun.net.www * @library .. - * @build HttpCallback TestHttpsServer ClosedChannelList HttpTransaction TunnelProxy + * @build HttpCallback TestHttpsServer ClosedChannelList + * HttpTransaction TunnelProxy + * @key intermittent * @run main/othervm B6216082 */ From 4d646ce63350d2c0e29069159adc93c95736aa69 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Tue, 8 Dec 2015 10:13:57 +0530 Subject: [PATCH 011/199] 8143404: Remove apple script engine code in jdk repository Reviewed-by: alanb, mchung --- jdk/make/lib/Lib-jdk.deploy.osx.gmk | 26 - .../apple/applescript/AppleScriptEngine.java | 389 --------- .../applescript/AppleScriptEngineFactory.java | 244 ------ .../AS_NS_ConversionUtils.h | 38 - .../AS_NS_ConversionUtils.m | 793 ------------------ .../libapplescriptengine/AppleScriptEngine.m | 199 ----- .../AppleScriptExecutionContext.h | 46 - .../AppleScriptExecutionContext.m | 165 ---- .../NS_Java_ConversionUtils.h | 33 - .../NS_Java_ConversionUtils.m | 145 ---- 10 files changed, 2078 deletions(-) delete mode 100644 jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngine.java delete mode 100644 jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngineFactory.java delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.h delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.m delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptEngine.m delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.h delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.m delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.h delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.m diff --git a/jdk/make/lib/Lib-jdk.deploy.osx.gmk b/jdk/make/lib/Lib-jdk.deploy.osx.gmk index a5c0eb1d41f..08125ce9d23 100644 --- a/jdk/make/lib/Lib-jdk.deploy.osx.gmk +++ b/jdk/make/lib/Lib-jdk.deploy.osx.gmk @@ -29,32 +29,6 @@ ifeq ($(OPENJDK_TARGET_OS), macosx) ################################################################################ - LIBAPPLESCRIPTENGINE_SRC := $(JDK_TOPDIR)/src/jdk.deploy.osx/macosx/native/libapplescriptengine - - $(eval $(call SetupNativeCompilation,BUILD_LIBAPPLESCRIPTENGINE, \ - LIBRARY := AppleScriptEngine, \ - OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ - SRC := $(LIBAPPLESCRIPTENGINE_SRC), \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) \ - -I$(LIBAPPLESCRIPTENGINE_SRC) \ - -I$(SUPPORT_OUTPUTDIR)/headers/jdk.deploy.osx, \ - DISABLED_WARNINGS_clang := implicit-function-declaration format, \ - LDFLAGS := $(LDFLAGS_JDKLIB) \ - $(call SET_SHARED_LIBRARY_ORIGIN), \ - LIBS := -framework Cocoa \ - -framework Carbon \ - -framework JavaNativeFoundation \ - $(JDKLIB_LIBS), \ - OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libAppleScriptEngine, \ - DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES))) - - $(BUILD_LIBAPPLESCRIPTENGINE): $(call FindLib, java.base, java) - - TARGETS += $(BUILD_LIBAPPLESCRIPTENGINE) - - ################################################################################ - LIBOSX_DIRS := $(JDK_TOPDIR)/src/jdk.deploy.osx/macosx/native/libosx LIBOSX_CFLAGS := -I$(LIBOSX_DIRS) \ -I$(JDK_TOPDIR)/src/java.desktop/macosx/native/libosxapp \ diff --git a/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngine.java b/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngine.java deleted file mode 100644 index 8ef8116e744..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngine.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2011, 2013, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package apple.applescript; - -import java.io.*; -import java.nio.file.Files; -import java.util.*; -import java.util.Map.Entry; - -import javax.script.*; - -/** - * AppleScriptEngine implements JSR 223 for AppleScript on Mac OS X - */ -public class AppleScriptEngine implements ScriptEngine { - private static native void initNative(); - - private static native long createContextFrom(final Object object); - private static native Object createObjectFrom(final long context); - private static native void disposeContext(final long context); - - private static native long evalScript(final String script, long contextptr); - private static native long evalScriptFromURL(final String filename, long contextptr); - - static { - System.loadLibrary("AppleScriptEngine"); - initNative(); - TRACE(""); - } - - static void checkSecurity() { - final SecurityManager securityManager = System.getSecurityManager(); - if (securityManager != null) securityManager.checkExec("/usr/bin/osascript"); - } - - static void TRACE(final String str) { -// System.out.println(AppleScriptEngine.class.getName() + "." + str); - } - - /** - * Accessor for the ScriptEngine's long name variable - * @return the long name of the ScriptEngine - */ - protected static String getEngine() { - TRACE("getEngine()"); - return AppleScriptEngineFactory.ENGINE_NAME; - } - - /** - * Accessor for the ScriptEngine's version - * @return the version of the ScriptEngine - */ - protected static String getEngineVersion() { - TRACE("getEngineVersion()"); - return AppleScriptEngineFactory.ENGINE_VERSION; - } - - /** - * Accessor for the ScriptEngine's short name - * @return the short name of the ScriptEngine - */ - protected static String getName() { - TRACE("getName()"); - return AppleScriptEngineFactory.ENGINE_SHORT_NAME; - } - - /** - * Accessor for the ScriptEngine's supported language name - * @return the language the ScriptEngine supports - */ - protected static String getLanguage() { - TRACE("getLanguage()"); - return AppleScriptEngineFactory.LANGUAGE; - } - - /** - * The no argument constructor sets up the object with default members, - * a factory for the engine and a fresh context. - * @see com.apple.applescript.AppleScriptEngine#init() - */ - public AppleScriptEngine() { - TRACE("()"); - // set our parent factory to be a new factory - factory = AppleScriptEngineFactory.getFactory(); - - // set up our noarg bindings - setContext(new SimpleScriptContext()); - put(ARGV, ""); - - init(); - } - - /** - * All AppleScriptEngines share the same ScriptEngineFactory - */ - private final ScriptEngineFactory factory; - - /** - * The local context for the AppleScriptEngine - */ - private ScriptContext context; - - /** - * The constructor taking a factory as an argument sets the parent factory for - * this engine to be the passed factory, and sets up the engine with a fresh context - * @param factory - * @see com.apple.applescript.AppleScriptEngine#init() - */ - public AppleScriptEngine(final ScriptEngineFactory factory) { - // inherit the factory passed to us - this.factory = factory; - - // set up our noarg bindings - setContext(new SimpleScriptContext()); - put(ARGV, ""); - - init(); - } - - /** - * The initializer populates the local context with some useful predefined variables: - *
  • javax_script_language_version - the version of AppleScript that the AppleScriptEngine supports.
  • - *
  • javax_script_language - "AppleScript" -- the language supported by the AppleScriptEngine.
  • - *
  • javax_script_engine - "AppleScriptEngine" -- the name of the ScriptEngine.
  • - *
  • javax_script_engine_version - the version of the AppleScriptEngine
  • - *
  • javax_script_argv - "" -- AppleScript does not take arguments from the command line
  • - *
  • javax_script_filename - "" -- the currently executing filename
  • - *
  • javax_script_name - "AppleScriptEngine" -- the short name of the AppleScriptEngine
  • - *
  • THREADING - null -- the AppleScriptEngine does not support concurrency, you will have to implement thread-safeness yourself.
- */ - private void init() { - TRACE("init()"); - // set up our context -/* TODO -- name of current executable? bad java documentation at: - * http://docs.oracle.com/javase/6/docs/api/javax/script/ScriptEngine.html#FILENAME */ - put(ScriptEngine.FILENAME, ""); - put(ScriptEngine.ENGINE, getEngine()); - put(ScriptEngine.ENGINE_VERSION, getEngineVersion()); - put(ScriptEngine.NAME, getName()); - put(ScriptEngine.LANGUAGE, getLanguage()); - put(ScriptEngine.LANGUAGE_VERSION, getLanguageVersion()); - - // TODO -- for now, err on the side of caution and say that we are NOT thread-safe - put("THREADING", null); - } - - /** - * Uses the AppleScriptEngine to get the local AppleScript version - * @return the version of AppleScript running on the system - */ - protected String getLanguageVersion() { - TRACE("AppleScriptEngine.getLanguageVersion()"); - try { - final Object result = eval("get the version of AppleScript"); - if (result instanceof String) return (String)result; - } catch (final ScriptException e) { e.printStackTrace(); } - return "unknown"; - } - - /** - * Implementation required by ScriptEngine parent
- * Returns the factory parent of this AppleScriptEngine - */ - public ScriptEngineFactory getFactory() { - return factory; - } - - /** - * Implementation required by ScriptEngine parent
- * Return the engine's context - * @return this ScriptEngine's context - */ - public ScriptContext getContext() { - return context; - } - - /** - * Implementation required by ScriptEngine parent
- * Set a new context for the engine - * @param context the new context to install in the engine - */ - public void setContext(final ScriptContext context) { - this.context = context; - } - - /** - * Implementation required by ScriptEngine parent
- * Create and return a new set of simple bindings. - * @return a new and empty set of bindings - */ - public Bindings createBindings() { - return new SimpleBindings(); - } - - /** - * Implementation required by ScriptEngine parent
- * Return the engines bindings for the context indicated by the argument. - * @param scope contextual scope to return. - * @return the bindings in the engine for the scope indicated by the parameter - */ - public Bindings getBindings(final int scope) { - return context.getBindings(scope); - } - - /** - * Implementation required by ScriptEngine parent
- * Sets the bindings for the indicated scope - * @param bindings a set of bindings to assign to the engine - * @param scope the scope that the passed bindings should be assigned to - */ - public void setBindings(final Bindings bindings, final int scope) { - context.setBindings(bindings, scope); - } - - /** - * Implementation required by ScriptEngine parent
- * Insert a key and value into the engine's bindings (scope: engine) - * @param key the key of the pair - * @param value the value of the pair - */ - public void put(final String key, final Object value) { - getBindings(ScriptContext.ENGINE_SCOPE).put(key, value); - } - - /** - * Implementation required by ScriptEngine parent
- * Get a value from the engine's bindings using a key (scope: engine) - * @param key the key of the pair - * @return the value of the pair - */ - public Object get(final String key) { - return getBindings(ScriptContext.ENGINE_SCOPE).get(key); - } - - /** - * Implementation required by ScriptEngine parent
- * Passes the Reader argument, as well as the engine's context to a lower evaluation function.
- * Prefers FileReader or BufferedReader wrapping FileReader as argument. - * @param reader a Reader to AppleScript source or compiled AppleScript - * @return an Object corresponding to the return value of the script - * @see com.apple.applescript.AppleScriptEngine#eval(Reader, ScriptContext) - */ - public Object eval(final Reader reader) throws ScriptException { - return eval(reader, getContext()); - } - - /** - * Implementation required by ScriptEngine parent
- * Uses the passed bindings as the context for executing the passed script. - * @param reader a stream to AppleScript source or compiled AppleScript - * @param bindings a Bindings object representing the contexts to execute inside - * @return the return value of the script - * @see com.apple.applescript.AppleScriptEngine#eval(Reader, ScriptContext) - */ - public Object eval(final Reader reader, final Bindings bindings) throws ScriptException { - final Bindings tmp = getContext().getBindings(ScriptContext.ENGINE_SCOPE); - getContext().setBindings(bindings, ScriptContext.ENGINE_SCOPE); - final Object retval = eval(reader); - getContext().setBindings(tmp, ScriptContext.ENGINE_SCOPE); - return retval; - } - - /** - * Implementation required by ScriptEngine parent
- * This function can execute either AppleScript source or compiled AppleScript and functions by writing the - * contents of the Reader to a temporary file and then executing it with the engine's context. - * @param reader - * @param scriptContext - * @return an Object corresponding to the return value of the script - */ - public Object eval(final Reader reader, final ScriptContext context) throws ScriptException { - checkSecurity(); - - // write our passed reader to a temporary file - File tmpfile; - FileWriter tmpwrite; - try { - tmpfile = Files.createTempFile("AppleScriptEngine.", ".scpt").toFile(); - tmpwrite = new FileWriter(tmpfile); - - // read in our input and write directly to tmpfile - /* TODO -- this may or may not be avoidable for certain Readers, - * if a filename can be grabbed, it would be faster to get that and - * use the underlying file than writing a temp file. - */ - int data; - while ((data = reader.read()) != -1) { - tmpwrite.write(data); - } - tmpwrite.close(); - - // set up our context business - final long contextptr = scriptContextToNSDictionary(context); - try { - final long retCtx = evalScriptFromURL("file://" + tmpfile.getCanonicalPath(), contextptr); - Object retVal = (retCtx == 0) ? null : createObjectFrom(retCtx); - disposeContext(retCtx); - return retVal; - } finally { - disposeContext(contextptr); - tmpfile.delete(); - } - } catch (final IOException e) { - throw new ScriptException(e); - } - } - - /** - * Implementation required by ScriptEngine parent
- * Evaluate an AppleScript script passed as a source string. Using the engine's built in context. - * @param script the string to execute. - * @return an Object representing the return value of the script - * @see com.apple.applescript.AppleScriptEngine#eval(String, ScriptContext) - */ - public Object eval(final String script) throws ScriptException { - return eval(script, getContext()); - } - - /** - * Implementation required by ScriptEngine parent
- * Evaluate an AppleScript script passed as a source string with a custom ScriptContext. - * @param script the AppleScript source to compile and execute. - * @param scriptContext the context to execute the script under - * @see com.apple.applescript.AppleScriptEngine#eval(String, ScriptContext) - */ - public Object eval(final String script, final Bindings bindings) throws ScriptException { - final Bindings tmp = getContext().getBindings(ScriptContext.ENGINE_SCOPE); - getContext().setBindings(bindings, ScriptContext.ENGINE_SCOPE); - - final Object retval = eval(script); - getContext().setBindings(tmp, ScriptContext.ENGINE_SCOPE); - - return retval; - } - - /** - * Implementation required by ScriptEngine parent - * @param script - * @param scriptContext - */ - public Object eval(final String script, final ScriptContext context) throws ScriptException { - checkSecurity(); - final long ctxPtr = scriptContextToNSDictionary(context); - try { - final long retCtx = evalScript(script, ctxPtr); - Object retVal = (retCtx == 0) ? null : createObjectFrom(retCtx); - disposeContext(retCtx); - return retVal; - } finally { - disposeContext(ctxPtr); - } - } - - /** - * Converts a ScriptContext into an NSDictionary - * @param context ScriptContext for the engine - * @return a pointer to an NSDictionary - */ - private long scriptContextToNSDictionary(final ScriptContext context) throws ScriptException { - final Map contextAsMap = new HashMap(); - for (final Entry e : context.getBindings(ScriptContext.ENGINE_SCOPE).entrySet()) { - contextAsMap.put(e.getKey().replaceAll("\\.", "_"), e.getValue()); - } - return createContextFrom(contextAsMap); - } -} diff --git a/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngineFactory.java b/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngineFactory.java deleted file mode 100644 index 8c50b9d9301..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngineFactory.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2011, 2013, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package apple.applescript; - -import java.security.*; -import java.util.*; -import javax.script.*; - -public class AppleScriptEngineFactory implements ScriptEngineFactory { - private static volatile boolean initialized = false; - - private static native void initNative(); - - static void TRACE(final String str) { -// System.out.println(AppleScriptEngineFactory.class.getName() + "." + str); - } - - /** - * The name of this ScriptEngine - */ - static final String ENGINE_NAME = "AppleScriptEngine"; - - /** - * The version of this ScriptEngine - */ - static final String ENGINE_VERSION = "1.1"; - - /** - * The name of this ScriptEngine (yes, again) - */ - static final String ENGINE_SHORT_NAME = ENGINE_NAME; - - /** - * The name of the language supported by this ScriptEngine - */ - static final String LANGUAGE = "AppleScript"; - - static ScriptEngineFactory getFactory() { - TRACE("getFactory()"); - return new AppleScriptEngineFactory(); - } - - /** - * Initialize a new AppleScriptEngineFactory, replete with a member AppleScriptEngine - */ - public AppleScriptEngineFactory() { - TRACE("()"); - } - - /** - * Returns the full name of the ScriptEngine. - * - * @return full name of the ScriptEngine - */ - @Override - public String getEngineName() { - TRACE("getEngineName()"); - return ENGINE_NAME; - } - - /** - * Returns the version of the ScriptEngine. - * - * @return version of the ScriptEngine - */ - @Override - public String getEngineVersion() { - TRACE("getEngineVersion()"); - return ENGINE_VERSION; - } - - /** - * Returns the name of the scripting language supported by this ScriptEngine. - * - * @return name of the language supported by the ScriptEngine(Factory) - */ - @Override - public String getLanguageName() { - TRACE("getLanguageName()"); - return LANGUAGE; - } - - /** - * Returns the version of the scripting language supported by this ScriptEngine(Factory). - * - * @return language version supported by the ScriptEngine(Factory) - */ - @Override - public String getLanguageVersion() { - TRACE("getLanguageVersion()"); - return AccessController.doPrivileged(new PrivilegedAction() { - public String run() { - final AppleScriptEngine engine = getScriptEngine(); - return engine.getLanguageVersion(); - } - }); - } - - /** - * Returns an immutable list of filename extensions, which generally identify - * scripts written in the language supported by this ScriptEngine. - * - * @return ArrayList of file extensions AppleScript associates with - */ - @Override - public List getExtensions() { - TRACE("getExtensions()"); - return Arrays.asList("scpt", "applescript", "app"); - } - - /** - * Returns an immutable list of mimetypes, associated with scripts - * that can be executed by the engine. - * - * @return ArrayList of mimetypes that AppleScript associates with - */ - @Override - public List getMimeTypes() { - TRACE("getMimeTypes()"); - return Arrays.asList("application/x-applescript", "text/plain", "text/applescript"); - } - - /** - * Returns an immutable list of short names for the ScriptEngine, - * which may be used to identify the ScriptEngine by the ScriptEngineManager. - * - * @return - */ - @Override - public List getNames() { - TRACE("getNames()"); - return Arrays.asList("AppleScriptEngine", "AppleScript", "OSA"); - } - - /** - * Returns a String which can be used to invoke a method of a Java - * object using the syntax of the supported scripting language. - * - * @param obj - * unused -- AppleScript does not support objects - * @param m - * function name - * @param args - * arguments to the function - * @return the AppleScript string calling the method - */ - @Override - public String getMethodCallSyntax(final String obj, final String fname, final String ... args) { -// StringBuilder builder = new StringBuilder(); -// builder.append("my " + fname + "("); -// // TODO -- do -// builder.append(")\n"); -// return builder.toString(); - - return null; - } - - /** - * Returns a String that can be used as a statement to display the specified String using the syntax of the supported scripting language. - * - * @param toDisplay - * @return - */ - @Override - public String getOutputStatement(final String toDisplay) { - // TODO -- this might even be good enough? XD - return getMethodCallSyntax(null, "print", toDisplay); - } - - /** - * Returns the value of an attribute whose meaning may be implementation-specific. - * - * @param key - * the key to look up - * @return the static preseeded value for the key in the ScriptEngine, if it exists, otherwise null - */ - @Override - public Object getParameter(final String key) { - final AppleScriptEngine engine = getScriptEngine(); - if (!engine.getBindings(ScriptContext.ENGINE_SCOPE).containsKey(key)) return null; - return engine.getBindings(ScriptContext.ENGINE_SCOPE).get(key); - } - - /** - * Returns A valid scripting language executable program with given statements. - * - * @param statements - * @return - */ - @Override - public String getProgram(final String ... statements) { - final StringBuilder program = new StringBuilder(); - for (final String statement : statements) { - program.append(statement + "\n"); - } - return program.toString(); - } - - /** - * Returns an instance of the ScriptEngine associated with this ScriptEngineFactory. - * - * @return new AppleScriptEngine with this factory as it's parent - */ - @Override - public AppleScriptEngine getScriptEngine() { - AppleScriptEngine.checkSecurity(); - ensureInitialized(); - - return new AppleScriptEngine(this); - } - - private static synchronized void ensureInitialized() { - if (!initialized) { - initialized = true; - - java.awt.Toolkit.getDefaultToolkit(); - System.loadLibrary("AppleScriptEngine"); - initNative(); - } - } -} diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.h b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.h deleted file mode 100644 index 3b428d222d7..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011, 2012, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 - - -// A 'collection' (responds to -objectEnumerator) is translated to an AS list. -// For any other object obj, this returns [[obj description] aeDescriptorValue], mainly -// intended for debugging purposes. -@interface NSObject (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *) aeDescriptorValue; -@end - -@interface NSAppleEventDescriptor (JavaAppleScriptEngineAdditions) -- (id) objCObjectValue; -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.m b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.m deleted file mode 100644 index 42c0da59d5e..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.m +++ /dev/null @@ -1,793 +0,0 @@ -/* - * Copyright (c) 2011, 2012, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -// -// Most of this is adapted from Ken Ferry's KFAppleScript Additions, contributed with permission -// http://homepage.mac.com/kenferry/software.html -// - -#import "AS_NS_ConversionUtils.h" - -#import -#import - - -@interface NSAppleEventDescriptor (JavaAppleScriptEngineAdditionsPrivate) - -// just returns self. This means that you can pass custom descriptors -// to -[NSAppleScript executeHandler:error:withParameters:]. -- (NSAppleEventDescriptor *)aeDescriptorValue; - -// working with primitive descriptor types -+ (id)descriptorWithInt16:(SInt16)val; -- (SInt16)int16Value; -+ (id)descriptorWithUnsignedInt32:(UInt32)val; -- (UInt32)unsignedInt32Value; -+ (id)descriptorWithFloat32:(Float32)val; -- (Float32)float32Value; -+ (id)descriptorWithFloat64:(Float64)val; -- (Float64)float64Value; -+ (id)descriptorWithLongDateTime:(LongDateTime)val; -- (LongDateTime)longDateTimeValue; - - -// These are the methods for converting AS objects to objective-C objects. -// -[NSAppleEventDescriptor objCObjectValue] is the general method for converting -// AS objects to ObjC objects, and is called by -[NSAppleScript executeHandler:error:withParameters:]. -// It does no work itself. It finds a handler based on the type of the descriptor and lets that -// handler object do the work. If there is no handler type registered for a the type of a descriptor, -// the raw descriptor is returned. -// -// You can designate a handlers for descriptor types with -// +[NSAppleEventDescriptor registerConversionHandler:selector:forDescriptorTypes:]. Please note -// that this method does _not_ retain the handler object (for now anyway). The selector should -// take a single argument, a descriptor to translate, and should return an object. An example such -// selector is @selector(dictionaryWithAEDesc:), for which the handler object would be [NSDictionary class]. -// -// A number of handlers are designated by default. The methods and objects can be easily inferred (or check -// the implementation), but the automatically handled types are -// typeUnicodeText, -// typeText, -// typeUTF8Text, -// typeCString, -// typeChar, -// typeBoolean, -// typeTrue, -// typeFalse, -// typeSInt16, -// typeSInt32, -// typeUInt32, -// typeSInt64, -// typeIEEE32BitFloatingPoint, -// typeIEEE64BitFloatingPoint, -// type128BitFloatingPoint, -// typeAEList, -// typeAERecord, -// typeLongDateTime, -// typeNull. -+ (void)registerConversionHandler:(id)anObject selector:(SEL)aSelector forDescriptorTypes:(DescType)firstType, ...; -+ (void) jaseSetUpHandlerDict; -@end - -// wrap the NSAppleEventDescriptor string methods -@interface NSString (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (NSString *)stringWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - -// wrap the NSAppleEventDescriptor longDateTime methods -@interface NSDate (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (NSDate *)dateWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - -// these are fairly complicated methods, due to having to try to match up the various -// AS number types (see NSAppleEventDescriptor for the primitive number methods) -// with NSNumber variants. For complete behavior it's best to look at the implementation. -// Some notes: -// NSNumbers created with numberWithBool should be correctly translated to AS booleans and vice versa. -// NSNumbers created with large integer types may have to be translated to AS doubles, -// so be careful if checking equality (you may have to check equality within epsilon). -// Since NSNumbers can't remember if they were created with an unsigned value, -// [[NSNumber numberWithUnsignedChar:255] aeDescriptorValue] is going to get you an AS integer -// with value -1. If you really need a descriptor with an unsigned value, you'll need to do it -// manually using the primitive methods on NSAppleEventDescriptor. The resulting descriptor -// can still be passed to AS with -[NSAppleScript executeHandler:error:withParameters:]. -@interface NSNumber (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (id)numberWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - -// Here we're following the behavior described in the CocoaScripting release note. -// -// NSPoint -> list of two numbers: {x, y} -// NSRange -> list of two numbers: {begin offset, end offset} -// NSRect -> list of four numbers: {left, bottom, right, top} -// NSSize -> list of two numbers: {width, height} -@interface NSValue (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -@end - -// No need for ObjC -> AS conversion here, we fall through to NSObject as a collection. -// For AS -> ObjC conversion, we build an array using the primitive list methods on -// NSAppleEventDescriptor. -@interface NSArray (JavaAppleScriptEngineAdditions) -+ (NSArray *)arrayWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - - -// Please see the CocoaScripting release note for behavior. It's kind of complicated. -// -// methods wrap the primitive record methods on NSAppleEventDescriptor. -@interface NSDictionary (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (NSDictionary *)dictionaryWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - -// be aware that a null descriptor does not correspond to the 'null' keyword in -// AppleScript - it's more like nothing at all. For example, the return -// from an empty handler. -@interface NSNull (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (NSNull *)nullWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - - -@interface NSNumber (JavaAppleScriptEngineAdditionsPrivate) -+ (id) jaseNumberWithSignedIntP:(void *)int_p byteCount:(int)bytes; -+ (id) jaseNumberWithUnsignedIntP:(void *)int_p byteCount:(int)bytes; -+ (id) jaseNumberWithFloatP:(void *)float_p byteCount:(int)bytes; -@end - - -@implementation NSObject (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - // collections go to lists - if (![self respondsToSelector:@selector(objectEnumerator)]) { - // encode the description as a fallback - this is pretty useless, only helpful for debugging - return [[self description] aeDescriptorValue]; - } - - NSAppleEventDescriptor *resultDesc = [NSAppleEventDescriptor listDescriptor]; - NSEnumerator *objectEnumerator = [(id)self objectEnumerator]; - - unsigned int i = 1; // apple event descriptors are 1-indexed - id currentObject; - while((currentObject = [objectEnumerator nextObject]) != nil) { - [resultDesc insertDescriptor:[currentObject aeDescriptorValue] atIndex:i++]; - } - - return resultDesc; -} - -@end - - -@implementation NSArray (JavaAppleScriptEngineAdditions) - -// don't need to override aeDescriptorValue, the NSObject will treat the array as a collection -+ (NSArray *)arrayWithAEDesc:(NSAppleEventDescriptor *)desc { - NSAppleEventDescriptor *listDesc = [desc coerceToDescriptorType:typeAEList]; - NSMutableArray *resultArray = [NSMutableArray array]; - - // apple event descriptors are 1-indexed - unsigned int listCount = [listDesc numberOfItems]; - unsigned int i; - for (i = 1; i <= listCount; i++) { - [resultArray addObject:[[listDesc descriptorAtIndex:i] objCObjectValue]]; - } - - return resultArray; -} - -@end - - -@implementation NSDictionary (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - NSAppleEventDescriptor *resultDesc = [NSAppleEventDescriptor recordDescriptor]; - NSMutableArray *userFields = [NSMutableArray array]; - NSArray *keys = [self allKeys]; - - unsigned int keyCount = [keys count]; - unsigned int i; - for (i = 0; i < keyCount; i++) { - id key = [keys objectAtIndex:i]; - - if ([key isKindOfClass:[NSNumber class]]) { - [resultDesc setDescriptor:[[self objectForKey:key] aeDescriptorValue] forKeyword:[(NSNumber *)key intValue]]; - } else if ([key isKindOfClass:[NSString class]]) { - [userFields addObject:key]; - [userFields addObject:[self objectForKey:key]]; - } - } - - if ([userFields count] > 0) { - [resultDesc setDescriptor:[userFields aeDescriptorValue] forKeyword:keyASUserRecordFields]; - } - - return resultDesc; -} - -+ (NSDictionary *)dictionaryWithAEDesc:(NSAppleEventDescriptor *)desc { - NSAppleEventDescriptor *recDescriptor = [desc coerceToDescriptorType:typeAERecord]; - NSMutableDictionary *resultDict = [NSMutableDictionary dictionary]; - - // NSAppleEventDescriptor uses 1 indexing - unsigned int recordCount = [recDescriptor numberOfItems]; - unsigned int recordIndex; - for (recordIndex = 1; recordIndex <= recordCount; recordIndex++) { - AEKeyword keyword = [recDescriptor keywordForDescriptorAtIndex:recordIndex]; - - if(keyword == keyASUserRecordFields) { - NSAppleEventDescriptor *listDescriptor = [recDescriptor descriptorAtIndex:recordIndex]; - - // NSAppleEventDescriptor uses 1 indexing - unsigned int listCount = [listDescriptor numberOfItems]; - unsigned int listIndex; - for (listIndex = 1; listIndex <= listCount; listIndex += 2) { - id keyObj = [[listDescriptor descriptorAtIndex:listIndex] objCObjectValue]; - id valObj = [[listDescriptor descriptorAtIndex:listIndex+1] objCObjectValue]; - - [resultDict setObject:valObj forKey:keyObj]; - } - } else { - id keyObj = [NSNumber numberWithInt:keyword]; - id valObj = [[recDescriptor descriptorAtIndex:recordIndex] objCObjectValue]; - - [resultDict setObject:valObj forKey:keyObj]; - } - } - - return resultDict; -} - -@end - - -@implementation NSString (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - return [NSAppleEventDescriptor descriptorWithString:self]; -} - -+ (NSString *)stringWithAEDesc:(NSAppleEventDescriptor *)desc { - return [desc stringValue]; -} - -+ (NSString *)versionWithAEDesc:(NSAppleEventDescriptor *)desc { - const AEDesc *aeDesc = [desc aeDesc]; - VersRec v; - AEGetDescData(aeDesc, &v, sizeof(v)); - return [[[NSString alloc] initWithBytes:&v.shortVersion[1] length:StrLength(v.shortVersion) encoding:NSUTF8StringEncoding] autorelease]; -} - -@end - - -@implementation NSNull (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - return [NSAppleEventDescriptor nullDescriptor]; -} - -+ (NSNull *)nullWithAEDesc:(NSAppleEventDescriptor *)desc { - return [NSNull null]; -} - -@end - - -@implementation NSDate (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - LongDateTime ldt; - UCConvertCFAbsoluteTimeToLongDateTime(CFDateGetAbsoluteTime((CFDateRef)self), &ldt); - return [NSAppleEventDescriptor descriptorWithLongDateTime:ldt]; -} - -+ (NSDate *)dateWithAEDesc:(NSAppleEventDescriptor *)desc { - CFAbsoluteTime absTime; - UCConvertLongDateTimeToCFAbsoluteTime([desc longDateTimeValue], &absTime); - NSDate *resultDate = (NSDate *)CFDateCreate(NULL, absTime); - return [resultDate autorelease]; -} - -@end - - - -static inline int areEqualEncodings(const char *enc1, const char *enc2) { - return (strcmp(enc1, enc2) == 0); -} - -@implementation NSNumber (JavaAppleScriptEngineAdditions) - --(id)jaseDescriptorValueWithFloatP:(void *)float_p byteCount:(int)bytes { - float floatVal; - if (bytes < sizeof(Float32)) { - floatVal = [self floatValue]; - float_p = &floatVal; - bytes = sizeof(floatVal); - } - - double doubleVal; - if (bytes > sizeof(Float64)) { - doubleVal = [self doubleValue]; - float_p = &doubleVal; - bytes = sizeof(doubleVal); - } - - if (bytes == sizeof(Float32)) { - return [NSAppleEventDescriptor descriptorWithFloat32:*(Float32 *)float_p]; - } - - if (bytes == sizeof(Float64)) { - return [NSAppleEventDescriptor descriptorWithFloat64:*(Float64 *)float_p]; - } - - [NSException raise:NSInvalidArgumentException - format:@"Cannot create an NSAppleEventDescriptor for float with %d bytes of data.", bytes]; - - return nil; -} - --(id)jaseDescriptorValueWithSignedIntP:(void *)int_p byteCount:(int)bytes { - int intVal; - - if (bytes < sizeof(SInt16)) { - intVal = [self intValue]; - int_p = &intVal; - bytes = sizeof(intVal); - } - - if (bytes == sizeof(SInt16)) { - return [NSAppleEventDescriptor descriptorWithInt16:*(SInt16 *)int_p]; - } - - if (bytes == sizeof(SInt32)) { - return [NSAppleEventDescriptor descriptorWithInt32:*(SInt32 *)int_p]; - } - - double val = [self doubleValue]; - return [self jaseDescriptorValueWithFloatP:&val byteCount:sizeof(val)]; -} - --(id)jaseDescriptorValueWithUnsignedIntP:(void *)int_p byteCount:(int)bytes { - unsigned int uIntVal; - - if (bytes < sizeof(UInt32)) { - uIntVal = [self unsignedIntValue]; - int_p = &uIntVal; - bytes = sizeof(uIntVal); - } - - if (bytes == sizeof(UInt32)) { - return [NSAppleEventDescriptor descriptorWithUnsignedInt32:*(UInt32 *)int_p]; - } - - double val = (double)[self unsignedLongLongValue]; - return [self jaseDescriptorValueWithFloatP:&val byteCount:sizeof(val)]; -} - -- (NSAppleEventDescriptor *)aeDescriptorValue { - // NSNumber is unfortunately complicated, because the applescript - // type we should use depends on the c type that our NSNumber corresponds to - - const char *type = [self objCType]; - - // convert - if (areEqualEncodings(type, @encode(BOOL))) { - return [NSAppleEventDescriptor descriptorWithBoolean:[self boolValue]]; - } - - if (areEqualEncodings(type, @encode(char))) { - char val = [self charValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(short))) { - short val = [self shortValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(int))) { - int val = [self intValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(long))) { - long val = [self longValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(long long))) { - long long val = [self longLongValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned char))) { - unsigned char val = [self unsignedCharValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned short))) { - unsigned short val = [self unsignedShortValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned int))) { - unsigned int val = [self unsignedIntValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned long))) { - unsigned long val = [self unsignedLongValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned long long))) { - unsigned long long val = [self unsignedLongLongValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(float))) { - float val = [self floatValue]; - return [self jaseDescriptorValueWithFloatP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(double))) { - double val = [self doubleValue]; - return [self jaseDescriptorValueWithFloatP:&val byteCount:sizeof(val)]; - } - - [NSException raise:@"jaseUnsupportedAEDescriptorConversion" - format:@"JavaAppleScriptEngineAdditions: conversion of an NSNumber with objCType '%s' to an aeDescriptor is not supported.", type]; - - return nil; -} - -+ (id)numberWithAEDesc:(NSAppleEventDescriptor *)desc { - DescType type = [desc descriptorType]; - - if ((type == typeTrue) || (type == typeFalse) || (type == typeBoolean)) { - return [NSNumber numberWithBool:[desc booleanValue]]; - } - - if (type == typeSInt16) { - SInt16 val = [desc int16Value]; - return [NSNumber jaseNumberWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (type == typeSInt32) { - SInt32 val = [desc int32Value]; - return [NSNumber jaseNumberWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (type == typeUInt32) { - UInt32 val = [desc unsignedInt32Value]; - return [NSNumber jaseNumberWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (type == typeIEEE32BitFloatingPoint) { - Float32 val = [desc float32Value]; - return [NSNumber jaseNumberWithFloatP:&val byteCount:sizeof(val)]; - } - - if (type == typeIEEE64BitFloatingPoint) { - Float64 val = [desc float64Value]; - return [NSNumber jaseNumberWithFloatP:&val byteCount:sizeof(val)]; - } - - // try to coerce to 64bit floating point - desc = [desc coerceToDescriptorType:typeIEEE64BitFloatingPoint]; - if (desc != nil) { - Float64 val = [desc float64Value]; - return [NSNumber jaseNumberWithFloatP:&val byteCount:sizeof(val)]; - } - - [NSException raise:@"jaseUnsupportedAEDescriptorConversion" - format:@"JavaAppleScriptEngineAdditions: conversion of an NSAppleEventDescriptor with objCType '%s' to an aeDescriptor is not supported.", type]; - - return nil; -} - -+ (id) jaseNumberWithSignedIntP:(void *)int_p byteCount:(int)bytes { - if (bytes == sizeof(char)) { - return [NSNumber numberWithChar:*(char *)int_p]; - } - - if (bytes == sizeof(short)) { - return [NSNumber numberWithShort:*(short *)int_p]; - } - - if (bytes == sizeof(int)) { - return [NSNumber numberWithInt:*(int *)int_p]; - } - - if (bytes == sizeof(long)) { - return [NSNumber numberWithLong:*(long *)int_p]; - } - - if (bytes == sizeof(long long)) { - return [NSNumber numberWithLongLong:*(long long *)int_p]; - } - - [NSException raise:NSInvalidArgumentException - format:@"NSNumber jaseNumberWithSignedIntP:byteCount: number with %i bytes not supported.", bytes]; - - return nil; -} - -+ (id) jaseNumberWithUnsignedIntP:(void *)int_p byteCount:(int)bytes { - if (bytes == sizeof(unsigned char)) { - return [NSNumber numberWithUnsignedChar:*(unsigned char *)int_p]; - } - - if (bytes == sizeof(unsigned short)) { - return [NSNumber numberWithUnsignedShort:*(unsigned short *)int_p]; - } - - if (bytes == sizeof(unsigned int)) { - return [NSNumber numberWithUnsignedInt:*(unsigned int *)int_p]; - } - - if (bytes == sizeof(unsigned long)) { - return [NSNumber numberWithUnsignedLong:*(unsigned long *)int_p]; - } - - if (bytes == sizeof(unsigned long long)) { - return [NSNumber numberWithUnsignedLongLong:*(unsigned long long *)int_p]; - } - - [NSException raise:NSInvalidArgumentException - format:@"NSNumber numberWithUnsignedInt:byteCount: number with %i bytes not supported.", bytes]; - - return nil; -} - -+ (id) jaseNumberWithFloatP:(void *)float_p byteCount:(int)bytes { - if (bytes == sizeof(float)) { - return [NSNumber numberWithFloat:*(float *)float_p]; - } - - if (bytes == sizeof(double)) { - return [NSNumber numberWithFloat:*(double *)float_p]; - } - - [NSException raise:NSInvalidArgumentException - format:@"NSNumber numberWithFloat:byteCount: floating point number with %i bytes not supported.", bytes]; - - return nil; -} - -@end - -@implementation NSValue (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - const char *type = [self objCType]; - - if (areEqualEncodings(type, @encode(NSSize))) { - NSSize size = [self sizeValue]; - return [[NSArray arrayWithObjects: - [NSNumber numberWithFloat:size.width], - [NSNumber numberWithFloat:size.height], nil] aeDescriptorValue]; - } - - if (areEqualEncodings(type, @encode(NSPoint))) { - NSPoint point = [self pointValue]; - return [[NSArray arrayWithObjects: - [NSNumber numberWithFloat:point.x], - [NSNumber numberWithFloat:point.y], nil] aeDescriptorValue]; - } - - if (areEqualEncodings(type, @encode(NSRange))) { - NSRange range = [self rangeValue]; - return [[NSArray arrayWithObjects: - [NSNumber numberWithUnsignedInt:range.location], - [NSNumber numberWithUnsignedInt:range.location + range.length], nil] aeDescriptorValue]; - } - - if (areEqualEncodings(type, @encode(NSRect))) { - NSRect rect = [self rectValue]; - return [[NSArray arrayWithObjects: - [NSNumber numberWithFloat:rect.origin.x], - [NSNumber numberWithFloat:rect.origin.y], - [NSNumber numberWithFloat:rect.origin.x + rect.size.width], - [NSNumber numberWithFloat:rect.origin.y + rect.size.height], nil] aeDescriptorValue]; - } - - [NSException raise:@"jaseUnsupportedAEDescriptorConversion" - format:@"JavaAppleScriptEngineAdditions: conversion of an NSNumber with objCType '%s' to an aeDescriptor is not supported.", type]; - - return nil; -} - -@end - - -@implementation NSImage (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - NSData *data = [self TIFFRepresentation]; - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeTIFF data:data]; -} - -+ (NSImage *)imageWithAEDesc:(NSAppleEventDescriptor *)desc { - const AEDesc *d = [desc aeDesc]; - NSMutableData *data = [NSMutableData dataWithLength:AEGetDescDataSize(d)]; - AEGetDescData(d, [data mutableBytes], [data length]); - return [[[NSImage alloc] initWithData:data] autorelease]; -} - -@end - - - -@implementation NSAppleEventDescriptor (JavaAppleScriptEngineAdditions) - -// we're going to leak this. It doesn't matter much for running apps, but -// for developers it might be nice to try to dispose of it (so it would not clutter the -// output when testing for leaks) -static NSMutableDictionary *handlerDict = nil; - -- (id)objCObjectValue { - if (handlerDict == nil) [NSAppleEventDescriptor jaseSetUpHandlerDict]; - - id returnObj; - DescType type = [self descriptorType]; - NSInvocation *handlerInvocation = [handlerDict objectForKey:[NSValue valueWithBytes:&type objCType:@encode(DescType)]]; - if (handlerInvocation == nil) { - if (type == typeType) { - DescType subType; - AEGetDescData([self aeDesc], &subType, sizeof(subType)); - if (subType == typeNull) return [NSNull null]; - } - // return raw apple event descriptor if no handler is registered - returnObj = self; - } else { - [handlerInvocation setArgument:&self atIndex:2]; - [handlerInvocation invoke]; - [handlerInvocation getReturnValue:&returnObj]; - } - - return returnObj; -} - -// FIXME - error checking, non nil handler -+ (void)registerConversionHandler:(id)anObject selector:(SEL)aSelector forDescriptorTypes:(DescType)firstType, ... { - if (handlerDict == nil) [NSAppleEventDescriptor jaseSetUpHandlerDict]; - - NSInvocation *handlerInvocation = [NSInvocation invocationWithMethodSignature:[anObject methodSignatureForSelector:aSelector]]; - [handlerInvocation setTarget:anObject]; - [handlerInvocation setSelector:aSelector]; - - DescType aType = firstType; - va_list typesList; - va_start(typesList, firstType); - do { - NSValue *type = [NSValue valueWithBytes:&aType objCType:@encode(DescType)]; - [handlerDict setObject:handlerInvocation forKey:type]; - } while((aType = va_arg(typesList, DescType)) != 0); - va_end(typesList); -} - - -- (NSAppleEventDescriptor *)aeDescriptorValue { - return self; -} - -+ (id)descriptorWithInt16:(SInt16)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeSInt16 bytes:&val length:sizeof(val)]; -} - -- (SInt16)int16Value { - SInt16 retValue; - [[[self coerceToDescriptorType:typeSInt16] data] getBytes:&retValue]; - return retValue; -} - -+ (id)descriptorWithUnsignedInt32:(UInt32)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeUInt32 bytes:&val length:sizeof(val)]; -} - -- (UInt32)unsignedInt32Value { - UInt32 retValue; - [[[self coerceToDescriptorType:typeUInt32] data] getBytes:&retValue]; - return retValue; -} - - -+ (id)descriptorWithFloat32:(Float32)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE32BitFloatingPoint bytes:&val length:sizeof(val)]; -} - -- (Float32)float32Value { - Float32 retValue; - [[[self coerceToDescriptorType:typeIEEE32BitFloatingPoint] data] getBytes:&retValue]; - return retValue; -} - - -+ (id)descriptorWithFloat64:(Float64)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE64BitFloatingPoint bytes:&val length:sizeof(val)]; -} - -- (Float64)float64Value { - Float64 retValue; - [[[self coerceToDescriptorType:typeIEEE64BitFloatingPoint] data] getBytes:&retValue]; - return retValue; -} - -+ (id)descriptorWithLongDateTime:(LongDateTime)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeLongDateTime bytes:&val length:sizeof(val)]; -} - -- (LongDateTime)longDateTimeValue { - LongDateTime retValue; - [[[self coerceToDescriptorType:typeLongDateTime] data] getBytes:&retValue]; - return retValue; -} - -+ (void)jaseSetUpHandlerDict { - handlerDict = [[NSMutableDictionary alloc] init]; - - // register default handlers - // types are culled from AEDataModel.h and AERegistry.h - - // string -> NSStrings - [NSAppleEventDescriptor registerConversionHandler:[NSString class] selector:@selector(stringWithAEDesc:) forDescriptorTypes: - typeUnicodeText, typeText, typeUTF8Text, typeCString, typeChar, nil]; - - // number/bool -> NSNumber - [NSAppleEventDescriptor registerConversionHandler:[NSNumber class] selector:@selector(numberWithAEDesc:) forDescriptorTypes: - typeBoolean, typeTrue, typeFalse, - typeSInt16, typeSInt32, typeUInt32, typeSInt64, - typeIEEE32BitFloatingPoint, typeIEEE64BitFloatingPoint, type128BitFloatingPoint, nil]; - - // list -> NSArray - [NSAppleEventDescriptor registerConversionHandler:[NSArray class] selector:@selector(arrayWithAEDesc:) forDescriptorTypes:typeAEList, nil]; - - // record -> NSDictionary - [NSAppleEventDescriptor registerConversionHandler:[NSDictionary class] selector:@selector(dictionaryWithAEDesc:) forDescriptorTypes:typeAERecord, nil]; - - // date -> NSDate - [NSAppleEventDescriptor registerConversionHandler:[NSDate class] selector:@selector(dateWithAEDesc:) forDescriptorTypes:typeLongDateTime, nil]; - - // images -> NSImage - [NSAppleEventDescriptor registerConversionHandler:[NSImage class] selector:@selector(imageWithAEDesc:) forDescriptorTypes: - typeTIFF, typeJPEG, typeGIF, typePict, typeIconFamily, typeIconAndMask, nil]; - - // vers -> NSString - [NSAppleEventDescriptor registerConversionHandler:[NSString class] selector:@selector(versionWithAEDesc:) forDescriptorTypes:typeVersion, nil]; - - // null -> NSNull - [NSAppleEventDescriptor registerConversionHandler:[NSNull class] selector:@selector(nullWithAEDesc:) forDescriptorTypes:typeNull, nil]; -} - -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptEngine.m b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptEngine.m deleted file mode 100644 index 33c8cdc379b..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptEngine.m +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2011, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 "apple_applescript_AppleScriptEngine.h" -#import "apple_applescript_AppleScriptEngineFactory.h" - -// Must include this before JavaNativeFoundation.h to get jni.h from build -#include "jni.h" -#include "jni_util.h" - -#import - -#import "NS_Java_ConversionUtils.h" -#import "AppleScriptExecutionContext.h" - -//#define DEBUG 1 - -/* - * Declare library specific JNI_Onload entry if static build - */ -DEF_STATIC_JNI_OnLoad - -/* - * Class: apple_applescript_AppleScriptEngineFactory - * Method: initNative - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_apple_applescript_AppleScriptEngineFactory_initNative -(JNIEnv *env, jclass clazz) -{ - return; -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: initNative - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_apple_applescript_AppleScriptEngine_initNative -(JNIEnv *env, jclass clazz) -{ - return; -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: createContextFrom - * Signature: (Ljava/lang/Object;)J - */ -JNIEXPORT jlong JNICALL Java_apple_applescript_AppleScriptEngine_createContextFrom -(JNIEnv *env, jclass clazz, jobject javaContext) -{ - NSObject *obj = nil; - -JNF_COCOA_ENTER(env); - - obj = [[JavaAppleScriptEngineCoercion coercer] coerceJavaObject:javaContext withEnv:env]; - -#ifdef DEBUG - NSLog(@"converted context: %@", obj); -#endif - - CFRetain(obj); - -JNF_COCOA_EXIT(env); - - return ptr_to_jlong(obj); -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: createObjectFrom - * Signature: (J)Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_apple_applescript_AppleScriptEngine_createObjectFrom -(JNIEnv *env, jclass clazz, jlong nativeContext) -{ - jobject obj = NULL; - -JNF_COCOA_ENTER(env); - - obj = [[JavaAppleScriptEngineCoercion coercer] coerceNSObject:(id)jlong_to_ptr(nativeContext) withEnv:env]; - -JNF_COCOA_EXIT(env); - - return obj; -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: disposeContext - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_apple_applescript_AppleScriptEngine_disposeContext -(JNIEnv *env, jclass clazz, jlong nativeContext) -{ - -JNF_COCOA_ENTER(env); - - id obj = (id)jlong_to_ptr(nativeContext); - if (obj != nil) CFRelease(obj); - -JNF_COCOA_EXIT(env); - -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: evalScript - * Signature: (Ljava/lang/String;J)J - */ -JNIEXPORT jlong JNICALL Java_apple_applescript_AppleScriptEngine_evalScript -(JNIEnv *env, jclass clazz, jstring ascript, jlong contextptr) -{ - id retval = nil; - -JNF_COCOA_ENTER(env); - - NSDictionary *ncontext = jlong_to_ptr(contextptr); - NSString *source = JNFJavaToNSString(env, ascript); - -#ifdef DEBUG - NSLog(@"evalScript(source:\"%@\" context: %@)", source, ncontext); -#endif - - AppleScriptExecutionContext *scriptInvocationCtx = [[[AppleScriptExecutionContext alloc] initWithSource:source context:ncontext] autorelease]; - retval = [scriptInvocationCtx invokeWithEnv:env]; - -#ifdef DEBUG - NSLog(@"returning: %@", retval); -#endif - - if (retval) CFRetain(retval); - -JNF_COCOA_EXIT(env); - - return ptr_to_jlong(retval); -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: evalScriptFromURL - * Signature: (Ljava/lang/String;J)J - */ -JNIEXPORT jlong JNICALL Java_apple_applescript_AppleScriptEngine_evalScriptFromURL -(JNIEnv *env, jclass clazz, jstring afilename, jlong contextptr) -{ - id retval = nil; - -JNF_COCOA_ENTER(env); - - NSDictionary *ncontext = jlong_to_ptr(contextptr); - NSString *filename = JNFJavaToNSString(env, afilename); - -#ifdef DEBUG - NSLog(@"evalScript(filename:\"%@\" context: %@)", filename, ncontext); -#endif - - AppleScriptExecutionContext *scriptInvocationCtx = [[[AppleScriptExecutionContext alloc] initWithFile:filename context:ncontext] autorelease]; - retval = [scriptInvocationCtx invokeWithEnv:env]; - -#ifdef DEBUG - NSLog(@"returning: %@", retval); -#endif - - if (retval) CFRetain(retval); - -JNF_COCOA_EXIT(env); - - return ptr_to_jlong(retval); -} diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.h b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.h deleted file mode 100644 index 863d0a711a0..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011, 2012, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 - - -@interface AppleScriptExecutionContext : NSObject { - NSString *source; - BOOL isFile; - NSDictionary *context; - NSDictionary *error; - id returnValue; -} - -@property (nonatomic, retain) NSString *source; -@property (nonatomic, retain) NSDictionary *context; -@property (nonatomic, retain) NSDictionary *error; -@property (nonatomic, retain) id returnValue; - -- (id) initWithSource:(NSString *)source context:(NSDictionary *)context; -- (id) initWithFile:(NSString *)filename context:(NSDictionary *)context; -- (id) invokeWithEnv:(JNIEnv *)env; - -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.m b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.m deleted file mode 100644 index 9b4d4d705e1..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.m +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2011, 2012, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 "AppleScriptExecutionContext.h" - -#import - -#import "AS_NS_ConversionUtils.h" - - -@implementation AppleScriptExecutionContext - -@synthesize source; -@synthesize context; -@synthesize error; -@synthesize returnValue; - -- (id) init:(NSString *)sourceIn context:(id)contextIn { - self = [super init]; - if (!self) return self; - - self.source = sourceIn; - self.context = contextIn; - self.returnValue = nil; - self.error = nil; - - return self; -} - -- (id) initWithSource:(NSString *)sourceIn context:(NSDictionary *)contextIn { - self = [self init:sourceIn context:contextIn]; - isFile = NO; - return self; -} - -- (id) initWithFile:(NSString *)filenameIn context:(NSDictionary *)contextIn { - self = [self init:filenameIn context:contextIn]; - isFile = YES; - return self; -} - -- (void) dealloc { - self.source = nil; - self.context = nil; - self.returnValue = nil; - self.error = nil; - - [super dealloc]; -} - -- (NSAppleScript *) scriptFromURL { - NSURL *url = [NSURL URLWithString:source]; - NSDictionary *err = nil; - NSAppleScript *script = [[[NSAppleScript alloc] initWithContentsOfURL:url error:(&err)] autorelease]; - if (err != nil) self.error = err; - return script; -} - -- (NSAppleScript *) scriptFromSource { - return [[[NSAppleScript alloc] initWithSource:source] autorelease]; -} - -- (NSAppleEventDescriptor *) functionInvocationEvent { - NSString *function = [[context objectForKey:@"javax_script_function"] description]; - if (function == nil) return nil; - - // wrap the arg in an array if it is not already a list - id args = [context objectForKey:@"javax_script_argv"]; - if (![args isKindOfClass:[NSArray class]]) { - args = [NSArray arrayWithObjects:args, nil]; - } - - // triangulate our target - int pid = [[NSProcessInfo processInfo] processIdentifier]; - NSAppleEventDescriptor* targetAddress = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID - bytes:&pid - length:sizeof(pid)]; - - // create the event to call a subroutine in the script - NSAppleEventDescriptor* event = [[NSAppleEventDescriptor alloc] initWithEventClass:kASAppleScriptSuite - eventID:kASSubroutineEvent - targetDescriptor:targetAddress - returnID:kAutoGenerateReturnID - transactionID:kAnyTransactionID]; - - // set up the handler - NSAppleEventDescriptor* subroutineDescriptor = [NSAppleEventDescriptor descriptorWithString:[function lowercaseString]]; - [event setParamDescriptor:subroutineDescriptor forKeyword:keyASSubroutineName]; - - // set up the arguments - [event setParamDescriptor:[args aeDescriptorValue] forKeyword:keyDirectObject]; - - return [event autorelease]; -} - -- (void) invoke { - // create our script - NSAppleScript *script = isFile ? [self scriptFromURL] : [self scriptFromSource]; - if (self.error != nil) return; - - // find out if we have a subroutine to call - NSAppleEventDescriptor *fxnInvkEvt = [self functionInvocationEvent]; - - // exec! - NSAppleEventDescriptor *desc = nil; - NSDictionary *err = nil; - if (fxnInvkEvt == nil) { - desc = [script executeAndReturnError:(&err)]; - } else { - desc = [script executeAppleEvent:fxnInvkEvt error:(&err)]; - } - - // if we encountered an exception, stash and bail - if (err != nil) { - self.error = err; - return; - } - - // convert to NSObjects, and return in ivar - self.returnValue = [desc objCObjectValue]; -} - -- (id) invokeWithEnv:(JNIEnv *)env { - BOOL useAnyThread = [@"any-thread" isEqual:[context valueForKey:@"javax_script_threading"]]; - - // check if we are already on the AppKit thread, if desired - if(pthread_main_np() || useAnyThread) { - [self invoke]; - } else { - [JNFRunLoop performOnMainThread:@selector(invoke) on:self withObject:nil waitUntilDone:YES]; - } - - // if we have an exception parked in our ivar, snarf the message (if there is one), and toss a ScriptException - if (self.error != nil) { - NSString *asErrString = [self.error objectForKey:NSAppleScriptErrorMessage]; - if (!asErrString) asErrString = @"AppleScriptEngine failed to execute script."; // usually when we fail to load a file - [JNFException raise:env as:"javax/script/ScriptException" reason:[asErrString UTF8String]]; - } - - return self.returnValue; -} - -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.h b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.h deleted file mode 100644 index 8e5bd24b766..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011, 2012, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 - - -@interface JavaAppleScriptEngineCoercion : NSObject - -+ (JNFTypeCoercer *) coercer; - -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.m b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.m deleted file mode 100644 index 6114ce7a3bb..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.m +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2011, 2012, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 "NS_Java_ConversionUtils.h" - -#import - - -@interface JavaAppleScriptBaseConverter : NSObject -@end - -@interface JavaAppleScriptImageConverter : NSObject -@end - -@interface JavaAppleScriptVersionConverter : NSObject -@end - -@interface JavaAppleScriptNullConverter : NSObject -@end - - -@implementation JavaAppleScriptEngineCoercion - -static JNFTypeCoercer *appleScriptCoercer = nil; - -+ (JNFTypeCoercer *) coercer { - if (appleScriptCoercer) return appleScriptCoercer; - - id asSpecificCoercions = [[JNFDefaultCoercions defaultCoercer] deriveCoercer]; - [asSpecificCoercions addCoercion:[[[JavaAppleScriptImageConverter alloc] init] autorelease] forNSClass:[NSImage class] javaClass:@"java/awt/Image"]; - [asSpecificCoercions addCoercion:[[[JavaAppleScriptVersionConverter alloc] init] autorelease] forNSClass:[NSAppleEventDescriptor class] javaClass:nil]; - [asSpecificCoercions addCoercion:[[[JavaAppleScriptNullConverter alloc] init] autorelease] forNSClass:[NSNull class] javaClass:nil]; - - return appleScriptCoercer = [asSpecificCoercions retain]; -} - -@end - - -// [NSObject description] <-> java.lang.Object.toString() -@implementation JavaAppleScriptBaseConverter - -// by default, bizzare NSObjects will have -description called on them, and passed back to Java like that -- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return JNFNSToJavaString(env, [obj description]); -} - -// by default, bizzare Java objects will be toString()'d and passed to AppleScript like that -- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return JNFObjectToString(env, obj); -} - -@end - - -// NSImage <-> apple.awt.CImage -@implementation JavaAppleScriptImageConverter - -static JNF_CLASS_CACHE(jc_CImage, "apple/awt/CImage"); -static JNF_STATIC_MEMBER_CACHE(jm_CImage_getCreator, jc_CImage, "getCreator", "()Lapple/awt/CImage$Creator;"); -static JNF_MEMBER_CACHE(jm_CImage_getNSImage, jc_CImage, "getNSImage", "()J"); - -static JNF_CLASS_CACHE(jc_CImage_Generator, "apple/awt/CImage$Creator"); -static JNF_MEMBER_CACHE(jm_CImage_Generator_createImageFromPtr, jc_CImage_Generator, "createImage", "(J)Ljava/awt/image/BufferedImage;"); -static JNF_MEMBER_CACHE(jm_CImage_Generator_createImageFromImg, jc_CImage_Generator, "createImage", "(Ljava/awt/Image;)Lapple/awt/CImage;"); - -- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - NSImage *img = (NSImage *)obj; - CFRetain(img); - jobject creator = JNFCallStaticObjectMethod(env, jm_CImage_getCreator); - jobject jobj = JNFCallObjectMethod(env, creator, jm_CImage_Generator_createImageFromPtr, ptr_to_jlong(img)); - return jobj; -} - -- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - jobject cimage = obj; - if (!JNFIsInstanceOf(env, obj, &jc_CImage)) { - jobject creator = JNFCallStaticObjectMethod(env, jm_CImage_getCreator); - cimage = JNFCallObjectMethod(env, creator, jm_CImage_Generator_createImageFromImg, obj); - } - - jlong nsImagePtr = JNFCallLongMethod(env, cimage, jm_CImage_getNSImage); - - NSImage *img = (NSImage *)jlong_to_ptr(nsImagePtr); - return [[img retain] autorelease]; -} - -@end - - -// NSAppleEventDescriptor('vers') -> java.lang.String -@implementation JavaAppleScriptVersionConverter - -- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - NSAppleEventDescriptor *desc = (NSAppleEventDescriptor *)obj; - - const AEDesc *aeDesc = [desc aeDesc]; - if (aeDesc->descriptorType == typeNull) { - return NULL; - } - - return JNFNSToJavaString(env, [obj description]); -} - -- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return nil; // there is no Java object that represents a "version" -} - -@end - - -// NSNull <-> null -@implementation JavaAppleScriptNullConverter - -- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return NULL; -} - -- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return nil; -} - -@end From bb6d5ef30c8b1c8b5ccc6366e3efd8318227abba Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Tue, 8 Dec 2015 12:05:51 +0100 Subject: [PATCH 012/199] 8144857: Intermittent build error building jdk/src/demo/solaris/jni/Poller/Poller.c Reviewed-by: dholmes --- jdk/make/CompileDemos.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/make/CompileDemos.gmk b/jdk/make/CompileDemos.gmk index 5bb6ed39d2a..423378389cc 100644 --- a/jdk/make/CompileDemos.gmk +++ b/jdk/make/CompileDemos.gmk @@ -459,7 +459,7 @@ ifeq ($(OPENJDK_TARGET_OS), solaris) # We can only compile native code after java has been compiled (since we # depend on generated .h files) $(SUPPORT_OUTPUTDIR)/demos/native/jni/Poller/Poller.o: \ - $(BUILD_DEMO_JAVA_POLLER_COMPILE_TARGETS) + $(BUILD_DEMO_JAVA_Poller_COMPILE_TARGET) # Copy to image $(SUPPORT_OUTPUTDIR)/demos/image/jni/Poller/README.txt: \ From 44a8a73eb6585a158e95946673d8ff6a943daca6 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Tue, 8 Dec 2015 09:25:01 -0800 Subject: [PATCH 013/199] 8142508: To bring j.u.z.ZipFile's native implementation to Java to remove the expensive jni cost and mmap crash risk Reviewed-by: coffeys --- jdk/make/mapfiles/libzip/mapfile-vers | 22 +- jdk/make/mapfiles/libzip/reorder-sparc | 16 - jdk/make/mapfiles/libzip/reorder-sparcv9 | 15 - jdk/make/mapfiles/libzip/reorder-x86 | 18 - .../share/classes/java/util/jar/JarFile.java | 5 +- .../share/classes/java/util/zip/ZipCoder.java | 21 +- .../share/classes/java/util/zip/ZipFile.java | 837 ++++++++++++++---- .../share/classes/java/util/zip/ZipUtils.java | 79 +- .../internal/misc/JavaUtilZipFileAccess.java | 1 + .../java.base/share/classes/sun/misc/VM.java | 3 - .../java.base/share/native/libzip/ZipFile.c | 406 --------- jdk/test/java/util/zip/ZipFile/ReadZip.java | 3 +- .../java/util/zip/ZipFile/TestZipFile.java | 361 ++++++++ 13 files changed, 1115 insertions(+), 672 deletions(-) delete mode 100644 jdk/src/java.base/share/native/libzip/ZipFile.c create mode 100644 jdk/test/java/util/zip/ZipFile/TestZipFile.java diff --git a/jdk/make/mapfiles/libzip/mapfile-vers b/jdk/make/mapfiles/libzip/mapfile-vers index ceace23f26d..c1ab48c10cf 100644 --- a/jdk/make/mapfiles/libzip/mapfile-vers +++ b/jdk/make/mapfiles/libzip/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 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 @@ -27,7 +27,6 @@ SUNWprivate_1.1 { global: - Java_java_util_jar_JarFile_getMetaInfEntryNames; Java_java_util_zip_Adler32_update; Java_java_util_zip_Adler32_updateBytes; Java_java_util_zip_Adler32_updateByteBuffer; @@ -48,25 +47,6 @@ SUNWprivate_1.1 { Java_java_util_zip_Inflater_initIDs; Java_java_util_zip_Inflater_reset; Java_java_util_zip_Inflater_setDictionary; - Java_java_util_zip_ZipFile_close; - Java_java_util_zip_ZipFile_getCommentBytes; - Java_java_util_zip_ZipFile_freeEntry; - Java_java_util_zip_ZipFile_getEntry; - Java_java_util_zip_ZipFile_getEntryBytes; - Java_java_util_zip_ZipFile_getEntryCrc; - Java_java_util_zip_ZipFile_getEntryCSize; - Java_java_util_zip_ZipFile_getEntryFlag; - Java_java_util_zip_ZipFile_getEntryMethod; - Java_java_util_zip_ZipFile_getEntrySize; - Java_java_util_zip_ZipFile_getEntryTime; - Java_java_util_zip_ZipFile_getNextEntry; - Java_java_util_zip_ZipFile_getZipMessage; - Java_java_util_zip_ZipFile_getTotal; - Java_java_util_zip_ZipFile_initIDs; - Java_java_util_zip_ZipFile_open; - Java_java_util_zip_ZipFile_read; - Java_java_util_zip_ZipFile_startsWithLOC; - ZIP_Close; ZIP_CRC32; ZIP_FindEntry; diff --git a/jdk/make/mapfiles/libzip/reorder-sparc b/jdk/make/mapfiles/libzip/reorder-sparc index 154e7998a53..63b2ad6fc28 100644 --- a/jdk/make/mapfiles/libzip/reorder-sparc +++ b/jdk/make/mapfiles/libzip/reorder-sparc @@ -16,30 +16,14 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; -text: .text%Java_java_util_zip_ZipFile_initIDs; -text: .text%Java_java_util_zip_ZipFile_open; -text: .text%Java_java_util_zip_ZipFile_getTotal; -text: .text%Java_java_util_zip_ZipFile_startsWithLOC; -text: .text%Java_java_util_zip_ZipFile_getEntry; -text: .text%Java_java_util_zip_ZipFile_freeEntry; -text: .text%Java_java_util_zip_ZipFile_getEntryTime; -text: .text%Java_java_util_zip_ZipFile_getEntryCrc; -text: .text%Java_java_util_zip_ZipFile_getEntryCSize; -text: .text%Java_java_util_zip_ZipFile_getEntrySize; -text: .text%Java_java_util_zip_ZipFile_getEntryFlag; -text: .text%Java_java_util_zip_ZipFile_getEntryMethod; -text: .text%Java_java_util_zip_ZipFile_getEntryBytes; text: .text%Java_java_util_zip_Inflater_initIDs; text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; text: .text%zcalloc; text: .text%Java_java_util_zip_Inflater_inflateBytes; -text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; -text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; text: .text%Java_java_util_zip_Inflater_reset; text: .text%Java_java_util_zip_Inflater_end; text: .text%inflateEnd; -text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; diff --git a/jdk/make/mapfiles/libzip/reorder-sparcv9 b/jdk/make/mapfiles/libzip/reorder-sparcv9 index 89690b8dabb..caca118de98 100644 --- a/jdk/make/mapfiles/libzip/reorder-sparcv9 +++ b/jdk/make/mapfiles/libzip/reorder-sparcv9 @@ -15,19 +15,6 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; -text: .text%Java_java_util_zip_ZipFile_initIDs; -text: .text%Java_java_util_zip_ZipFile_open; -text: .text%Java_java_util_zip_ZipFile_getTotal; -text: .text%Java_java_util_zip_ZipFile_startsWithLOC; -text: .text%Java_java_util_zip_ZipFile_getEntry; -text: .text%Java_java_util_zip_ZipFile_freeEntry; -text: .text%Java_java_util_zip_ZipFile_getEntryTime; -text: .text%Java_java_util_zip_ZipFile_getEntryCrc; -text: .text%Java_java_util_zip_ZipFile_getEntryCSize; -text: .text%Java_java_util_zip_ZipFile_getEntrySize; -text: .text%Java_java_util_zip_ZipFile_getEntryFlag; -text: .text%Java_java_util_zip_ZipFile_getEntryMethod; -text: .text%Java_java_util_zip_ZipFile_getEntryBytes; text: .text%Java_java_util_zip_Inflater_initIDs; text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; @@ -35,7 +22,6 @@ text: .text%zcalloc; text: .text%inflateReset; text: .text%Java_java_util_zip_Inflater_inflateBytes; text: .text%inflate; -text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; @@ -43,6 +29,5 @@ text: .text%ZIP_ReadEntry; text: .text%InflateFully; text: .text%inflateEnd; text: .text%Java_java_util_zip_Inflater_reset; -text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; text: .text%Java_java_util_zip_Inflater_end; diff --git a/jdk/make/mapfiles/libzip/reorder-x86 b/jdk/make/mapfiles/libzip/reorder-x86 index 746948315eb..dfd57c7752e 100644 --- a/jdk/make/mapfiles/libzip/reorder-x86 +++ b/jdk/make/mapfiles/libzip/reorder-x86 @@ -16,34 +16,16 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; -text: .text%Java_java_util_zip_ZipFile_initIDs; -text: .text%Java_java_util_zip_ZipFile_open; -text: .text%Java_java_util_zip_ZipFile_getTotal; -text: .text%Java_java_util_zip_ZipFile_startsWithLOC; -text: .text%Java_java_util_zip_ZipFile_getEntry; -text: .text%Java_java_util_zip_ZipFile_freeEntry; -text: .text%Java_java_util_zip_ZipFile_getEntryTime; -text: .text%Java_java_util_zip_ZipFile_getEntryCrc; -text: .text%Java_java_util_zip_ZipFile_getEntryCSize; -text: .text%Java_java_util_zip_ZipFile_getEntrySize; -text: .text%Java_java_util_zip_ZipFile_getEntryFlag; -text: .text%Java_java_util_zip_ZipFile_getEntryMethod; -text: .text%Java_java_util_zip_ZipFile_getEntryBytes; -text: .text%Java_java_util_zip_Inflater_initIDs; -text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; text: .text%zcalloc; text: .text%inflateReset; text: .text%Java_java_util_zip_Inflater_inflateBytes; text: .text%inflate; -text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; -text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; text: .text%ZIP_ReadEntry; text: .text%InflateFully; text: .text%inflateEnd; text: .text%Java_java_util_zip_Inflater_reset; -text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; text: .text%Java_java_util_zip_Inflater_end; diff --git a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java index 6d16a517ac9..62734ceefbd 100644 --- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java @@ -203,7 +203,10 @@ class JarFile extends ZipFile { return man; } - private native String[] getMetaInfEntryNames(); + private String[] getMetaInfEntryNames() { + return jdk.internal.misc.SharedSecrets.getJavaUtilZipFileAccess() + .getMetaInfEntryNames((ZipFile)this); + } /** * Returns the {@code JarEntry} for the given entry name or diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java b/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java index b920b820e03..243d6e8c065 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -43,7 +43,7 @@ import sun.nio.cs.ArrayEncoder; final class ZipCoder { - String toString(byte[] ba, int length) { + String toString(byte[] ba, int off, int length) { CharsetDecoder cd = decoder().reset(); int len = (int)(length * cd.maxCharsPerByte()); char[] ca = new char[len]; @@ -53,12 +53,12 @@ final class ZipCoder { // CodingErrorAction.REPLACE mode. ZipCoder uses // REPORT mode. if (isUTF8 && cd instanceof ArrayDecoder) { - int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca); + int clen = ((ArrayDecoder)cd).decode(ba, off, length, ca); if (clen == -1) // malformed throw new IllegalArgumentException("MALFORMED"); return new String(ca, 0, clen); } - ByteBuffer bb = ByteBuffer.wrap(ba, 0, length); + ByteBuffer bb = ByteBuffer.wrap(ba, off, length); CharBuffer cb = CharBuffer.wrap(ca); CoderResult cr = cd.decode(bb, cb, true); if (!cr.isUnderflow()) @@ -69,8 +69,12 @@ final class ZipCoder { return new String(ca, 0, cb.position()); } + String toString(byte[] ba, int length) { + return toString(ba, 0, length); + } + String toString(byte[] ba) { - return toString(ba, ba.length); + return toString(ba, 0, ba.length); } byte[] getBytes(String s) { @@ -111,13 +115,16 @@ final class ZipCoder { return utf8.getBytes(s); } - String toStringUTF8(byte[] ba, int len) { + return toStringUTF8(ba, 0, len); + } + + String toStringUTF8(byte[] ba, int off, int len) { if (isUTF8) - return toString(ba, len); + return toString(ba, off, len); if (utf8 == null) utf8 = new ZipCoder(StandardCharsets.UTF_8); - return utf8.toString(ba, len); + return utf8.toString(ba, off, len); } boolean isUTF8() { diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java index 4e3a6d20417..561023c30e7 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -30,14 +30,22 @@ import java.io.InputStream; import java.io.IOException; import java.io.EOFException; import java.io.File; +import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.Path; +import java.nio.file.Files; + import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Deque; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.NoSuchElementException; import java.util.Spliterator; import java.util.Spliterators; @@ -47,7 +55,9 @@ import java.util.stream.StreamSupport; import jdk.internal.misc.JavaUtilZipFileAccess; import jdk.internal.misc.SharedSecrets; +import static java.util.zip.ZipConstants.*; import static java.util.zip.ZipConstants64.*; +import static java.util.zip.ZipUtils.*; /** * This class is used to read entries from a zip file. @@ -60,11 +70,11 @@ import static java.util.zip.ZipConstants64.*; */ public class ZipFile implements ZipConstants, Closeable { - private long jzfile; // address of jzfile data + private final String name; // zip file name - private final int total; // total number of entries - private final boolean locsig; // if zip file starts with LOCSIG (usually true) private volatile boolean closeRequested = false; + private Source zsrc; + private ZipCoder zc; private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; @@ -83,23 +93,6 @@ class ZipFile implements ZipConstants, Closeable { */ public static final int OPEN_DELETE = 0x4; - static { - /* Zip library is loaded from System.initializeSystemClass */ - initIDs(); - } - - private static native void initIDs(); - - private static final boolean usemmap; - - static { - // A system prpperty to disable mmap use to avoid vm crash when - // in-use zip file is accidently overwritten by others. - String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping"); - usemmap = (prop == null || - !(prop.length() == 0 || prop.equalsIgnoreCase("true"))); - } - /** * Opens a zip file for reading. * @@ -165,8 +158,6 @@ class ZipFile implements ZipConstants, Closeable { this(file, OPEN_READ); } - private ZipCoder zc; - /** * Opens a new {@code ZipFile} to read from the specified * {@code File} object in the specified mode. The mode argument @@ -214,16 +205,13 @@ class ZipFile implements ZipConstants, Closeable { sm.checkDelete(name); } } - if (charset == null) - throw new NullPointerException("charset is null"); + Objects.requireNonNull(charset, "charset"); this.zc = ZipCoder.get(charset); + this.name = name; long t0 = System.nanoTime(); - jzfile = open(name, mode, file.lastModified(), usemmap); + this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0); sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0); sun.misc.PerfCounter.getZipFileCount().increment(); - this.name = name; - this.total = getTotal(jzfile); - this.locsig = startsWithLOC(jzfile); } /** @@ -257,6 +245,7 @@ class ZipFile implements ZipConstants, Closeable { /** * Opens a ZIP file for reading given the specified File object. + * * @param file the ZIP file to be opened for reading * @param charset * The {@linkplain java.nio.charset.Charset charset} to be @@ -287,10 +276,10 @@ class ZipFile implements ZipConstants, Closeable { public String getComment() { synchronized (this) { ensureOpen(); - byte[] bcomm = getCommentBytes(jzfile); - if (bcomm == null) + if (zsrc.comment == null) { return null; - return zc.toString(bcomm, bcomm.length); + } + return zc.toString(zsrc.comment); } } @@ -303,38 +292,27 @@ class ZipFile implements ZipConstants, Closeable { * @throws IllegalStateException if the zip file has been closed */ public ZipEntry getEntry(String name) { - if (name == null) { - throw new NullPointerException("name"); - } - long jzentry = 0; + Objects.requireNonNull(name, "name"); synchronized (this) { ensureOpen(); - jzentry = getEntry(jzfile, zc.getBytes(name), true); - if (jzentry != 0) { - ZipEntry ze = getZipEntry(name, jzentry); - freeEntry(jzfile, jzentry); - return ze; + int pos = zsrc.getEntryPos(zc.getBytes(name), true); + if (pos != -1) { + return getZipEntry(name, pos); } } return null; } - private static native long getEntry(long jzfile, byte[] name, - boolean addSlash); - - // freeEntry releases the C jzentry struct. - private static native void freeEntry(long jzfile, long jzentry); - - // the outstanding inputstreams that need to be closed, + // The outstanding inputstreams that need to be closed, // mapped to the inflater objects they use. private final Map streams = new WeakHashMap<>(); /** * Returns an input stream for reading the contents of the specified * zip file entry. - * - *

Closing this ZIP file will, in turn, close all input - * streams that have been returned by invocations of this method. + *

+ * Closing this ZIP file will, in turn, close all input streams that + * have been returned by invocations of this method. * * @param entry the zip file entry * @return the input stream for reading the contents of the specified @@ -344,37 +322,38 @@ class ZipFile implements ZipConstants, Closeable { * @throws IllegalStateException if the zip file has been closed */ public InputStream getInputStream(ZipEntry entry) throws IOException { - if (entry == null) { - throw new NullPointerException("entry"); - } - long jzentry = 0; + Objects.requireNonNull(entry, "entry"); + int pos = -1; ZipFileInputStream in = null; synchronized (this) { ensureOpen(); if (!zc.isUTF8() && (entry.flag & EFS) != 0) { - jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); + pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false); } else { - jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); + pos = zsrc.getEntryPos(zc.getBytes(entry.name), false); } - if (jzentry == 0) { + if (pos == -1) { return null; } - in = new ZipFileInputStream(jzentry); - - switch (getEntryMethod(jzentry)) { + in = new ZipFileInputStream(zsrc.cen, pos); + switch (CENHOW(zsrc.cen, pos)) { case STORED: synchronized (streams) { streams.put(in, null); } return in; case DEFLATED: + // Inflater likes a bit of slack // MORE: Compute good size for inflater stream: - long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack - if (size > 65536) size = 8192; - if (size <= 0) size = 4096; + long size = CENLEN(zsrc.cen, pos) + 2; + if (size > 65536) { + size = 8192; + } + if (size <= 0) { + size = 4096; + } Inflater inf = getInflater(); - InputStream is = - new ZipFileInflaterInputStream(in, inf, (int)size); + InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size); synchronized (streams) { streams.put(is, inf); } @@ -447,8 +426,8 @@ class ZipFile implements ZipConstants, Closeable { private Inflater getInflater() { Inflater inf; synchronized (inflaterCache) { - while (null != (inf = inflaterCache.poll())) { - if (false == inf.ended()) { + while ((inf = inflaterCache.poll()) != null) { + if (!inf.ended()) { return inf; } } @@ -460,7 +439,7 @@ class ZipFile implements ZipConstants, Closeable { * Releases the specified inflater to the list of available inflaters. */ private void releaseInflater(Inflater inf) { - if (false == inf.ended()) { + if (!inf.ended()) { inf.reset(); synchronized (inflaterCache) { inflaterCache.add(inf); @@ -469,7 +448,7 @@ class ZipFile implements ZipConstants, Closeable { } // List of available Inflater objects for decompression - private Deque inflaterCache = new ArrayDeque<>(); + private final Deque inflaterCache = new ArrayDeque<>(); /** * Returns the path name of the ZIP file. @@ -493,7 +472,7 @@ class ZipFile implements ZipConstants, Closeable { public boolean hasNext() { synchronized (ZipFile.this) { ensureOpen(); - return i < total; + return i < zsrc.total; } } @@ -504,28 +483,11 @@ class ZipFile implements ZipConstants, Closeable { public ZipEntry next() { synchronized (ZipFile.this) { ensureOpen(); - if (i >= total) { + if (i >= zsrc.total) { throw new NoSuchElementException(); } - long jzentry = getNextEntry(jzfile, i++); - if (jzentry == 0) { - String message; - if (closeRequested) { - message = "ZipFile concurrently closed"; - } else { - message = getZipMessage(ZipFile.this.jzfile); - } - throw new ZipError("jzentry == 0" + - ",\n jzfile = " + ZipFile.this.jzfile + - ",\n total = " + ZipFile.this.total + - ",\n name = " + ZipFile.this.name + - ",\n i = " + i + - ",\n message = " + message - ); - } - ZipEntry ze = getZipEntry(null, jzentry); - freeEntry(jzfile, jzentry); - return ze; + // each "entry" has 3 ints in table entries + return getZipEntry(null, zsrc.getEntryPos(i++ * 3)); } } @@ -559,48 +521,53 @@ class ZipFile implements ZipConstants, Closeable { Spliterator.IMMUTABLE | Spliterator.NONNULL), false); } - private ZipEntry getZipEntry(String name, long jzentry) { + /* Checks ensureOpen() before invoke this method */ + private ZipEntry getZipEntry(String name, int pos) { + byte[] cen = zsrc.cen; ZipEntry e = new ZipEntry(); - e.flag = getEntryFlag(jzentry); // get the flag first + int nlen = CENNAM(cen, pos); + int elen = CENEXT(cen, pos); + int clen = CENCOM(cen, pos); + e.flag = CENFLG(cen, pos); // get the flag first if (name != null) { e.name = name; } else { - byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME); if (!zc.isUTF8() && (e.flag & EFS) != 0) { - e.name = zc.toStringUTF8(bname, bname.length); + e.name = zc.toStringUTF8(cen, pos + CENHDR, nlen); } else { - e.name = zc.toString(bname, bname.length); + e.name = zc.toString(cen, pos + CENHDR, nlen); } } - e.xdostime = getEntryTime(jzentry); - e.crc = getEntryCrc(jzentry); - e.size = getEntrySize(jzentry); - e.csize = getEntryCSize(jzentry); - e.method = getEntryMethod(jzentry); - e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false); - byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT); - if (bcomm == null) { - e.comment = null; - } else { + e.xdostime = CENTIM(cen, pos); + e.crc = CENCRC(cen, pos); + e.size = CENLEN(cen, pos); + e.csize = CENSIZ(cen, pos); + e.method = CENHOW(cen, pos); + if (elen != 0) { + e.setExtra0(Arrays.copyOfRange(cen, pos + CENHDR + nlen, + pos + CENHDR + nlen + elen), true); + } + if (clen != 0) { if (!zc.isUTF8() && (e.flag & EFS) != 0) { - e.comment = zc.toStringUTF8(bcomm, bcomm.length); + e.comment = zc.toStringUTF8(cen, pos + CENHDR + nlen + elen, clen); } else { - e.comment = zc.toString(bcomm, bcomm.length); + e.comment = zc.toString(cen, pos + CENHDR + nlen + elen, clen); } } return e; } - private static native long getNextEntry(long jzfile, int i); - /** * Returns the number of entries in the ZIP file. + * * @return the number of entries in the ZIP file * @throws IllegalStateException if the zip file has been closed */ public int size() { - ensureOpen(); - return total; + synchronized (this) { + ensureOpen(); + return zsrc.total; + } } /** @@ -612,14 +579,15 @@ class ZipFile implements ZipConstants, Closeable { * @throws IOException if an I/O error has occurred */ public void close() throws IOException { - if (closeRequested) + if (closeRequested) { return; + } closeRequested = true; synchronized (this) { // Close streams, release their inflaters synchronized (streams) { - if (false == streams.isEmpty()) { + if (!streams.isEmpty()) { Map copy = new HashMap<>(streams); streams.clear(); for (Map.Entry e : copy.entrySet()) { @@ -631,21 +599,17 @@ class ZipFile implements ZipConstants, Closeable { } } } - // Release cached inflaters - Inflater inf; synchronized (inflaterCache) { - while (null != (inf = inflaterCache.poll())) { + Inflater inf; + while ((inf = inflaterCache.poll()) != null) { inf.end(); } } - - if (jzfile != 0) { - // Close the zip file - long zf = this.jzfile; - jzfile = 0; - - close(zf); + // Release zip src + if (zsrc != null) { + Source.close(zsrc); + zsrc = null; } } } @@ -668,14 +632,11 @@ class ZipFile implements ZipConstants, Closeable { close(); } - private static native void close(long jzfile); - private void ensureOpen() { if (closeRequested) { throw new IllegalStateException("zip file closed"); } - - if (jzfile == 0) { + if (zsrc == null) { throw new IllegalStateException("The object is not initialized."); } } @@ -691,23 +652,86 @@ class ZipFile implements ZipConstants, Closeable { * (possibly compressed) zip file entry. */ private class ZipFileInputStream extends InputStream { - private volatile boolean zfisCloseRequested = false; - protected long jzentry; // address of jzentry data + private volatile boolean closeRequested = false; private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry - ZipFileInputStream(long jzentry) { - pos = 0; - rem = getEntryCSize(jzentry); - size = getEntrySize(jzentry); - this.jzentry = jzentry; + ZipFileInputStream(byte[] cen, int cenpos) throws IOException { + rem = CENSIZ(cen, cenpos); + size = CENLEN(cen, cenpos); + pos = CENOFF(cen, cenpos); + // zip64 + if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL || + pos == ZIP64_MAGICVAL) { + checkZIP64(cen, cenpos); + } + // negative for lazy initialization, see getDataOffset(); + pos = - (pos + ZipFile.this.zsrc.locpos); + } + + private void checkZIP64(byte[] cen, int cenpos) throws IOException { + int off = cenpos + CENHDR + CENNAM(cen, cenpos); + int end = off + CENEXT(cen, cenpos); + while (off + 4 < end) { + int tag = get16(cen, off); + int sz = get16(cen, off + 2); + off += 4; + if (off + sz > end) // invalid data + break; + if (tag == EXTID_ZIP64) { + if (size == ZIP64_MAGICVAL) { + if (sz < 8 || (off + 8) > end) + break; + size = get64(cen, off); + sz -= 8; + off += 8; + } + if (rem == ZIP64_MAGICVAL) { + if (sz < 8 || (off + 8) > end) + break; + rem = get64(cen, off); + sz -= 8; + off += 8; + } + if (pos == ZIP64_MAGICVAL) { + if (sz < 8 || (off + 8) > end) + break; + pos = get64(cen, off); + sz -= 8; + off += 8; + } + break; + } + off += sz; + } + } + + /* The Zip file spec explicitly allows the LOC extra data size to + * be different from the CEN extra data size. Since we cannot trust + * the CEN extra data size, we need to read the LOC to determine + * the entry data offset. + */ + private long initDataOffset() throws IOException { + if (pos <= 0) { + byte[] loc = new byte[LOCHDR]; + pos = -pos; + int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos); + if (len != LOCHDR) { + throw new ZipException("ZipFile error reading zip file"); + } + if (LOCSIG(loc) != LOCSIG) { + throw new ZipException("ZipFile invalid LOC header (bad signature)"); + } + pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc); + } + return pos; } public int read(byte b[], int off, int len) throws IOException { synchronized (ZipFile.this) { - long rem = this.rem; - long pos = this.pos; + ensureOpenOrZipException(); + initDataOffset(); if (rem == 0) { return -1; } @@ -717,14 +741,10 @@ class ZipFile implements ZipConstants, Closeable { if (len > rem) { len = (int) rem; } - - // Check if ZipFile open - ensureOpenOrZipException(); - len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, - off, len); + len = ZipFile.this.zsrc.readAt(b, off, len, pos); if (len > 0) { - this.pos = (pos + len); - this.rem = (rem - len); + pos += len; + rem -= len; } } if (rem == 0) { @@ -742,11 +762,16 @@ class ZipFile implements ZipConstants, Closeable { } } - public long skip(long n) { - if (n > rem) - n = rem; - pos += n; - rem -= n; + public long skip(long n) throws IOException { + synchronized (ZipFile.this) { + ensureOpenOrZipException(); + initDataOffset(); + if (n > rem) { + n = rem; + } + pos += n; + rem -= n; + } if (rem == 0) { close(); } @@ -762,17 +787,11 @@ class ZipFile implements ZipConstants, Closeable { } public void close() { - if (zfisCloseRequested) + if (closeRequested) { return; - zfisCloseRequested = true; - - rem = 0; - synchronized (ZipFile.this) { - if (jzentry != 0 && ZipFile.this.jzfile != 0) { - freeEntry(ZipFile.this.jzfile, jzentry); - jzentry = 0; - } } + closeRequested = true; + rem = 0; synchronized (streams) { streams.remove(this); } @@ -787,40 +806,492 @@ class ZipFile implements ZipConstants, Closeable { SharedSecrets.setJavaUtilZipFileAccess( new JavaUtilZipFileAccess() { public boolean startsWithLocHeader(ZipFile zip) { - return zip.startsWithLocHeader(); + return zip.zsrc.locsig; } - } + public String[] getMetaInfEntryNames(ZipFile zip) { + return zip.getMetaInfEntryNames(); + } + } ); } - /** - * Returns {@code true} if, and only if, the zip file begins with {@code - * LOCSIG}. + /* + * Returns an array of strings representing the names of all entries + * that begin with "META-INF/" (case ignored). This method is used + * in JarFile, via SharedSecrets, as an optimization when looking up + * manifest and signature file entries. Returns null if no entries + * were found. */ - private boolean startsWithLocHeader() { - return locsig; + private String[] getMetaInfEntryNames() { + synchronized (this) { + ensureOpen(); + if (zsrc.metanames.size() == 0) { + return null; + } + String[] names = new String[zsrc.metanames.size()]; + byte[] cen = zsrc.cen; + for (int i = 0; i < names.length; i++) { + int pos = zsrc.metanames.get(i); + names[i] = zc.toStringUTF8(cen, pos + CENHDR, CENNAM(cen, pos)); + } + return names; + } } - private static native long open(String name, int mode, long lastModified, - boolean usemmap) throws IOException; - private static native int getTotal(long jzfile); - private static native boolean startsWithLOC(long jzfile); - private static native int read(long jzfile, long jzentry, - long pos, byte[] b, int off, int len); + private static class Source { + private final Key key; // the key in files + private int refs = 1; - // access to the native zentry object - private static native long getEntryTime(long jzentry); - private static native long getEntryCrc(long jzentry); - private static native long getEntryCSize(long jzentry); - private static native long getEntrySize(long jzentry); - private static native int getEntryMethod(long jzentry); - private static native int getEntryFlag(long jzentry); - private static native byte[] getCommentBytes(long jzfile); + private RandomAccessFile zfile; // zfile of the underlying zip file + private byte[] cen; // CEN & ENDHDR + private long locpos; // position of first LOC header (usually 0) + private byte[] comment; // zip file comment + // list of meta entries in META-INF dir + private ArrayList metanames = new ArrayList<>(); + private final boolean locsig; // true, if zip file starts with LOCSIG (usually true) - private static final int JZENTRY_NAME = 0; - private static final int JZENTRY_EXTRA = 1; - private static final int JZENTRY_COMMENT = 2; - private static native byte[] getEntryBytes(long jzentry, int type); + // A Hashmap for all entries. + // + // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR, + // We might have a lot of these in a typical system. In order to save space we don't + // keep the name in memory, but merely remember a 32 bit {@code hash} value of the + // entry name and its offset {@code pos} in the central directory hdeader. + // + // private static class Entry { + // int hash; // 32 bit hashcode on name + // int next; // hash chain: index into entries + // int pos; // Offset of central directory file header + // } + // private Entry[] entries; // array of hashed cen entry + // + // To reduce the total size of entries further, we use a int[] here to store 3 "int" + // {@code hash}, {@code next and {@code "pos for each entry. The entry can then be + // referred by their index of their positions in the {@code entries}. + // + private int[] entries; // array of hashed cen entry + private int addEntry(int index, int hash, int next, int pos) { + entries[index++] = hash; + entries[index++] = next; + entries[index++] = pos; + return index; + } + private int getEntryHash(int index) { return entries[index]; } + private int getEntryNext(int index) { return entries[index + 1]; } + private int getEntryPos(int index) { return entries[index + 2]; } + private static final int ZIP_ENDCHAIN = -1; + private int total; // total number of entries + private int[] table; // Hash chain heads: indexes into entries + private int tablelen; // number of hash heads - private static native String getZipMessage(long jzfile); + private static class Key { + BasicFileAttributes attrs; + File file; + + public Key(File file, BasicFileAttributes attrs) { + this.attrs = attrs; + this.file = file; + } + + public int hashCode() { + long t = attrs.lastModifiedTime().toMillis(); + return ((int)(t ^ (t >>> 32))) + file.hashCode(); + } + + public boolean equals(Object obj) { + if (obj instanceof Key) { + Key key = (Key)obj; + if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) { + return false; + } + Object fk = attrs.fileKey(); + if (fk != null) { + return fk.equals(key.attrs.fileKey()); + } else { + return file.equals(key.file); + } + } + return false; + } + } + private static final HashMap files = new HashMap<>(); + + public static Source get(File file, boolean toDelete) throws IOException { + Key key = new Key(file, + Files.readAttributes(file.toPath(), BasicFileAttributes.class)); + Source src = null; + synchronized (files) { + src = files.get(key); + if (src != null) { + src.refs++; + return src; + } + } + src = new Source(key, toDelete); + synchronized (files) { + if (files.containsKey(key)) { // someone else put in first + src.close(); // close the newly created one + src = files.get(key); + src.refs++; + return src; + } + files.put(key, src); + return src; + } + } + + private static void close(Source src) throws IOException { + synchronized (files) { + if (--src.refs == 0) { + files.remove(src.key); + src.close(); + } + } + } + + private Source(Key key, boolean toDelete) throws IOException { + this.key = key; + this.zfile = new RandomAccessFile(key.file, "r"); + if (toDelete) { + key.file.delete(); + } + initCEN(-1); + byte[] buf = new byte[4]; + readFullyAt(buf, 0, 4, 0); + this.locsig = (LOCSIG(buf) != LOCSIG); + } + + private void close() throws IOException { + zfile.close(); + zfile = null; + cen = null; + entries = null; + table = null; + metanames = null; + } + + private static final int BUF_SIZE = 8192; + private final int readFullyAt(byte[] buf, int off, int len, long pos) + throws IOException + { + synchronized(zfile) { + zfile.seek(pos); + int N = len; + while (N > 0) { + int n = Math.min(BUF_SIZE, N); + zfile.readFully(buf, off, n); + off += n; + N -= n; + } + return len; + } + } + + private final int readAt(byte[] buf, int off, int len, long pos) + throws IOException + { + synchronized(zfile) { + zfile.seek(pos); + return zfile.read(buf, off, len); + } + } + + private static final int hashN(byte[] a, int off, int len) { + int h = 1; + while (len-- > 0) { + h = 31 * h + a[off++]; + } + return h; + } + + private static final int hash_append(int hash, byte b) { + return hash * 31 + b; + } + + private static class End { + int centot; // 4 bytes + long cenlen; // 4 bytes + long cenoff; // 4 bytes + long endpos; // 4 bytes + } + + /* + * Searches for end of central directory (END) header. The contents of + * the END header will be read and placed in endbuf. Returns the file + * position of the END header, otherwise returns -1 if the END header + * was not found or an error occurred. + */ + private End findEND() throws IOException { + long ziplen = zfile.length(); + if (ziplen <= 0) + zerror("zip file is empty"); + End end = new End(); + byte[] buf = new byte[READBLOCKSZ]; + long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0; + long minPos = minHDR - (buf.length - ENDHDR); + for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) { + int off = 0; + if (pos < 0) { + // Pretend there are some NUL bytes before start of file + off = (int)-pos; + Arrays.fill(buf, 0, off, (byte)0); + } + int len = buf.length - off; + if (readFullyAt(buf, off, len, pos + off) != len ) { + zerror("zip END header not found"); + } + // Now scan the block backwards for END header signature + for (int i = buf.length - ENDHDR; i >= 0; i--) { + if (buf[i+0] == (byte)'P' && + buf[i+1] == (byte)'K' && + buf[i+2] == (byte)'\005' && + buf[i+3] == (byte)'\006') { + // Found ENDSIG header + byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR); + end.centot = ENDTOT(endbuf); + end.cenlen = ENDSIZ(endbuf); + end.cenoff = ENDOFF(endbuf); + end.endpos = pos + i; + int comlen = ENDCOM(endbuf); + if (end.endpos + ENDHDR + comlen != ziplen) { + // ENDSIG matched, however the size of file comment in it does + // not match the real size. One "common" cause for this problem + // is some "extra" bytes are padded at the end of the zipfile. + // Let's do some extra verification, we don't care about the + // performance in this situation. + byte[] sbuf = new byte[4]; + long cenpos = end.endpos - end.cenlen; + long locpos = cenpos - end.cenoff; + if (cenpos < 0 || + locpos < 0 || + readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 || + GETSIG(sbuf) != CENSIG || + readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 || + GETSIG(sbuf) != LOCSIG) { + continue; + } + } + if (comlen > 0) { // this zip file has comlen + comment = new byte[comlen]; + if (readFullyAt(comment, 0, comlen, end.endpos + ENDHDR) != comlen) { + zerror("zip comment read failed"); + } + } + if (end.cenlen == ZIP64_MAGICVAL || + end.cenoff == ZIP64_MAGICVAL || + end.centot == ZIP64_MAGICCOUNT) + { + // need to find the zip64 end; + try { + byte[] loc64 = new byte[ZIP64_LOCHDR]; + if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) + != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) { + return end; + } + long end64pos = ZIP64_LOCOFF(loc64); + byte[] end64buf = new byte[ZIP64_ENDHDR]; + if (readFullyAt(end64buf, 0, end64buf.length, end64pos) + != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) { + return end; + } + // end64 found, re-calcualte everything. + end.cenlen = ZIP64_ENDSIZ(end64buf); + end.cenoff = ZIP64_ENDOFF(end64buf); + end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g + end.endpos = end64pos; + } catch (IOException x) {} // no zip64 loc/end + } + return end; + } + } + } + zerror("zip END header not found"); + return null; //make compiler happy + } + + // Reads zip file central directory. + private void initCEN(int knownTotal) throws IOException { + if (knownTotal == -1) { + End end = findEND(); + if (end.endpos == 0) { + locpos = 0; + total = 0; + entries = new int[0]; + cen = null; + return; // only END header present + } + if (end.cenlen > end.endpos) + zerror("invalid END header (bad central directory size)"); + long cenpos = end.endpos - end.cenlen; // position of CEN table + // Get position of first local file (LOC) header, taking into + // account that there may be a stub prefixed to the zip file. + locpos = cenpos - end.cenoff; + if (locpos < 0) { + zerror("invalid END header (bad central directory offset)"); + } + // read in the CEN and END + cen = new byte[(int)(end.cenlen + ENDHDR)]; + if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { + zerror("read CEN tables failed"); + } + total = end.centot; + } else { + total = knownTotal; + } + // hash table for entries + entries = new int[total * 3]; + tablelen = ((total/2) | 1); // Odd -> fewer collisions + table = new int[tablelen]; + Arrays.fill(table, ZIP_ENDCHAIN); + int idx = 0; + int hash = 0; + int next = -1; + + // list for all meta entries + metanames = new ArrayList<>(); + + // Iterate through the entries in the central directory + int i = 0; + int hsh = 0; + int pos = 0; + int limit = cen.length - ENDHDR; + while (pos + CENHDR <= limit) { + if (i >= total) { + // This will only happen if the zip file has an incorrect + // ENDTOT field, which usually means it contains more than + // 65535 entries. + initCEN(countCENHeaders(cen, limit)); + return; + } + if (CENSIG(cen, pos) != CENSIG) + zerror("invalid CEN header (bad signature)"); + int method = CENHOW(cen, pos); + int nlen = CENNAM(cen, pos); + int elen = CENEXT(cen, pos); + int clen = CENCOM(cen, pos); + if ((CENFLG(cen, pos) & 1) != 0) + zerror("invalid CEN header (encrypted entry)"); + if (method != STORED && method != DEFLATED) + zerror("invalid CEN header (bad compression method: " + method + ")"); + if (pos + CENHDR + nlen > limit) + zerror("invalid CEN header (bad header size)"); + // Record the CEN offset and the name hash in our hash cell. + hash = hashN(cen, pos + CENHDR, nlen); + hsh = (hash & 0x7fffffff) % tablelen; + next = table[hsh]; + table[hsh] = idx; + idx = addEntry(idx, hash, next, pos); + // Adds name to metanames. + if (isMetaName(cen, pos + CENHDR, nlen)) { + metanames.add(pos); + } + // skip ext and comment + pos += (CENHDR + nlen + elen + clen); + i++; + } + total = i; + if (pos + ENDHDR != cen.length) { + zerror("invalid CEN header (bad header size)"); + } + } + + private static void zerror(String msg) throws ZipException { + throw new ZipException(msg); + } + + /* + * Returns the {@code pos} of the zip cen entry corresponding to the + * specified entry name, or -1 if not found. + */ + private int getEntryPos(byte[] name, boolean addSlash) { + if (total == 0) { + return -1; + } + int hsh = hashN(name, 0, name.length); + int idx = table[(hsh & 0x7fffffff) % tablelen]; + /* + * This while loop is an optimization where a double lookup + * for name and name+/ is being performed. The name char + * array has enough room at the end to try again with a + * slash appended if the first table lookup does not succeed. + */ + while(true) { + /* + * Search down the target hash chain for a entry whose + * 32 bit hash matches the hashed name. + */ + while (idx != ZIP_ENDCHAIN) { + if (getEntryHash(idx) == hsh) { + // The CEN name must match the specfied one + int pos = getEntryPos(idx); + if (name.length == CENNAM(cen, pos)) { + boolean matched = true; + int nameoff = pos + CENHDR; + for (int i = 0; i < name.length; i++) { + if (name[i] != cen[nameoff++]) { + matched = false; + break; + } + } + if (matched) { + return pos; + } + } + } + idx = getEntryNext(idx); + } + /* If not addSlash, or slash is already there, we are done */ + if (!addSlash || name[name.length - 1] == '/') { + return -1; + } + /* Add slash and try once more */ + name = Arrays.copyOf(name, name.length + 1); + name[name.length - 1] = '/'; + hsh = hash_append(hsh, (byte)'/'); + //idx = table[hsh % tablelen]; + idx = table[(hsh & 0x7fffffff) % tablelen]; + addSlash = false; + } + } + + private static byte[] metainf = new byte[] { + 'M', 'E', 'T', 'A', '-', 'I' , 'N', 'F', '/', + }; + + /* + * Returns true if the specified entry's name begins with the string + * "META-INF/" irrespective of case. + */ + private static boolean isMetaName(byte[] name, int off, int len) { + if (len < 9 || (name[off] != 'M' && name[off] != 'm')) { // sizeof("META-INF/") - 1 + return false; + } + off++; + for (int i = 1; i < metainf.length; i++) { + byte c = name[off++]; + // Avoid toupper; it's locale-dependent + if (c >= 'a' && c <= 'z') { + c += 'A' - 'a'; + } + if (metainf[i] != c) { + return false; + } + } + return true; + } + + /* + * Counts the number of CEN headers in a central directory extending + * from BEG to END. Might return a bogus answer if the zip file is + * corrupt, but will not crash. + */ + static int countCENHeaders(byte[] cen, int end) { + int count = 0; + int pos = 0; + while (pos + CENHDR <= end) { + count++; + pos += (CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos)); + } + return count; + } + } } diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java index 81882fdcec6..a6632f0fa83 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java @@ -31,6 +31,8 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.concurrent.TimeUnit; +import static java.util.zip.ZipConstants.ENDHDR; + class ZipUtils { // used to adjust values between Windows and java epoch @@ -133,7 +135,7 @@ class ZipUtils { * The bytes are assumed to be in Intel (little-endian) byte order. */ public static final int get16(byte b[], int off) { - return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); + return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); } /** @@ -160,4 +162,79 @@ class ZipUtils { public static final int get32S(byte b[], int off) { return (get16(b, off) | (get16(b, off+2) << 16)); } + + // fields access methods + static final int CH(byte[] b, int n) { + return b[n] & 0xff ; + } + + static final int SH(byte[] b, int n) { + return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8); + } + + static final long LG(byte[] b, int n) { + return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL; + } + + static final long LL(byte[] b, int n) { + return (LG(b, n)) | (LG(b, n + 4) << 32); + } + + static final long GETSIG(byte[] b) { + return LG(b, 0); + } + + // local file (LOC) header fields + static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature + static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract + static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags + static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method + static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time + static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data + static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size + static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size + static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length + static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length + + // extra local (EXT) header fields + static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data + static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size + static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size + + // end of central directory header (END) fields + static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk + static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries + static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size + static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset + static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment + static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);} + + // zip64 end of central directory recoder fields + static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk + static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries + static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size + static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset + static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset + + // central directory header (CEN) fields + static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); } + static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); } + static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); } + static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); } + static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);} + static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);} + static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);} + static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);} + static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);} + static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);} + static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);} + static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);} + static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);} + static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);} + static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);} + static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);} + + // The END header is followed by a variable length comment of size < 64k. + static final long END_MAXLEN = 0xFFFF + ENDHDR; + static final int READBLOCKSZ = 128; } diff --git a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java index df10fda22ca..9b9b1e85788 100644 --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java @@ -29,5 +29,6 @@ import java.util.zip.ZipFile; public interface JavaUtilZipFileAccess { public boolean startsWithLocHeader(ZipFile zip); + public String[] getMetaInfEntryNames(ZipFile zip); } diff --git a/jdk/src/java.base/share/classes/sun/misc/VM.java b/jdk/src/java.base/share/classes/sun/misc/VM.java index 0c75f10c657..37dc3b38fa1 100644 --- a/jdk/src/java.base/share/classes/sun/misc/VM.java +++ b/jdk/src/java.base/share/classes/sun/misc/VM.java @@ -274,9 +274,6 @@ public class VM { // used by java.lang.Integer.IntegerCache props.remove("java.lang.Integer.IntegerCache.high"); - // used by java.util.zip.ZipFile - props.remove("sun.zip.disableMemoryMapping"); - // used by sun.launcher.LauncherHelper props.remove("sun.java.launcher.diag"); } diff --git a/jdk/src/java.base/share/native/libzip/ZipFile.c b/jdk/src/java.base/share/native/libzip/ZipFile.c deleted file mode 100644 index d7a21a6cf88..00000000000 --- a/jdk/src/java.base/share/native/libzip/ZipFile.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) 1998, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -/* - * Native method support for java.util.zip.ZipFile - */ - -#include -#include -#include -#include -#include -#include -#include "jlong.h" -#include "jvm.h" -#include "jni.h" -#include "jni_util.h" -#include "zip_util.h" -#ifdef WIN32 -#include "io_util_md.h" -#else -#include "io_util.h" -#endif - -#include "java_util_zip_ZipFile.h" -#include "java_util_jar_JarFile.h" - -#define DEFLATED 8 -#define STORED 0 - -static jfieldID jzfileID; - -static int OPEN_READ = java_util_zip_ZipFile_OPEN_READ; -static int OPEN_DELETE = java_util_zip_ZipFile_OPEN_DELETE; - - -/* - * Declare library specific JNI_Onload entry if static build - */ -DEF_STATIC_JNI_OnLoad - -JNIEXPORT void JNICALL -Java_java_util_zip_ZipFile_initIDs(JNIEnv *env, jclass cls) -{ - jzfileID = (*env)->GetFieldID(env, cls, "jzfile", "J"); - assert(jzfileID != 0); -} - -static void -ThrowZipException(JNIEnv *env, const char *msg) -{ - jstring s = NULL; - jobject x; - - if (msg != NULL) { - s = JNU_NewStringPlatform(env, msg); - } - if (s != NULL) { - x = JNU_NewObjectByName(env, - "java/util/zip/ZipException", - "(Ljava/lang/String;)V", s); - if (x != NULL) { - (*env)->Throw(env, x); - } - } -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_open(JNIEnv *env, jclass cls, jstring name, - jint mode, jlong lastModified, - jboolean usemmap) -{ - const char *path = JNU_GetStringPlatformChars(env, name, 0); - char *msg = 0; - jlong result = 0; - int flag = 0; - jzfile *zip = 0; - - if (mode & OPEN_READ) flag |= O_RDONLY; - - if (path != 0) { - zip = ZIP_Get_From_Cache(path, &msg, lastModified); - if (zip == 0 && msg == 0) { - ZFILE zfd = 0; -#ifdef WIN32 - if (mode & OPEN_DELETE) flag |= O_TEMPORARY; - zfd = winFileHandleOpen(env, name, flag); - if (zfd == -1) { - /* Exception already pending. */ - goto finally; - } -#else - zfd = open(path, flag, 0); - if (zfd < 0) { - throwFileNotFoundException(env, name); - goto finally; - } - if (mode & OPEN_DELETE) { - unlink(path); - } -#endif - zip = ZIP_Put_In_Cache0(path, zfd, &msg, lastModified, usemmap); - } - - if (zip != 0) { - result = ptr_to_jlong(zip); - } else if (msg != 0) { - ThrowZipException(env, msg); - free(msg); - } else if (errno == ENOMEM) { - JNU_ThrowOutOfMemoryError(env, 0); - } else { - ThrowZipException(env, "error in opening zip file"); - } -finally: - JNU_ReleaseStringPlatformChars(env, name, path); - } - return result; -} - -JNIEXPORT jint JNICALL -Java_java_util_zip_ZipFile_getTotal(JNIEnv *env, jclass cls, jlong zfile) -{ - jzfile *zip = jlong_to_ptr(zfile); - - return zip->total; -} - -JNIEXPORT jboolean JNICALL -Java_java_util_zip_ZipFile_startsWithLOC(JNIEnv *env, jclass cls, jlong zfile) -{ - jzfile *zip = jlong_to_ptr(zfile); - - return zip->locsig; -} - -JNIEXPORT void JNICALL -Java_java_util_zip_ZipFile_close(JNIEnv *env, jclass cls, jlong zfile) -{ - ZIP_Close(jlong_to_ptr(zfile)); -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile, - jbyteArray name, jboolean addSlash) -{ -#define MAXNAME 1024 - jzfile *zip = jlong_to_ptr(zfile); - jsize ulen = (*env)->GetArrayLength(env, name); - char buf[MAXNAME+2], *path; - jzentry *ze; - - if (ulen > MAXNAME) { - path = malloc(ulen + 2); - if (path == 0) { - JNU_ThrowOutOfMemoryError(env, 0); - return 0; - } - } else { - path = buf; - } - (*env)->GetByteArrayRegion(env, name, 0, ulen, (jbyte *)path); - path[ulen] = '\0'; - ze = ZIP_GetEntry2(zip, path, (jint)ulen, addSlash); - if (path != buf) { - free(path); - } - return ptr_to_jlong(ze); -} - -JNIEXPORT void JNICALL -Java_java_util_zip_ZipFile_freeEntry(JNIEnv *env, jclass cls, jlong zfile, - jlong zentry) -{ - jzfile *zip = jlong_to_ptr(zfile); - jzentry *ze = jlong_to_ptr(zentry); - ZIP_FreeEntry(zip, ze); -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getNextEntry(JNIEnv *env, jclass cls, jlong zfile, - jint n) -{ - jzentry *ze = ZIP_GetNextEntry(jlong_to_ptr(zfile), n); - return ptr_to_jlong(ze); -} - -JNIEXPORT jint JNICALL -Java_java_util_zip_ZipFile_getEntryMethod(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return ze->csize != 0 ? DEFLATED : STORED; -} - -JNIEXPORT jint JNICALL -Java_java_util_zip_ZipFile_getEntryFlag(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return ze->flag; -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntryCSize(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return ze->csize != 0 ? ze->csize : ze->size; -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntrySize(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return ze->size; -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntryTime(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return (jlong)ze->time & 0xffffffffUL; -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntryCrc(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return (jlong)ze->crc & 0xffffffffUL; -} - -JNIEXPORT jbyteArray JNICALL -Java_java_util_zip_ZipFile_getCommentBytes(JNIEnv *env, - jclass cls, - jlong zfile) -{ - jzfile *zip = jlong_to_ptr(zfile); - jbyteArray jba = NULL; - - if (zip->comment != NULL) { - if ((jba = (*env)->NewByteArray(env, zip->clen)) == NULL) - return NULL; - (*env)->SetByteArrayRegion(env, jba, 0, zip->clen, (jbyte*)zip->comment); - } - return jba; -} - -JNIEXPORT jbyteArray JNICALL -Java_java_util_zip_ZipFile_getEntryBytes(JNIEnv *env, - jclass cls, - jlong zentry, jint type) -{ - jzentry *ze = jlong_to_ptr(zentry); - int len = 0; - jbyteArray jba = NULL; - switch (type) { - case java_util_zip_ZipFile_JZENTRY_NAME: - if (ze->name != 0) { - len = (int)ze->nlen; - // Unlike for extra and comment, we never return null for - // an (extremely rarely seen) empty name - if ((jba = (*env)->NewByteArray(env, len)) == NULL) - break; - (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte *)ze->name); - } - break; - case java_util_zip_ZipFile_JZENTRY_EXTRA: - if (ze->extra != 0) { - unsigned char *bp = (unsigned char *)&ze->extra[0]; - len = (bp[0] | (bp[1] << 8)); - if (len <= 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) - break; - (*env)->SetByteArrayRegion(env, jba, 0, len, &ze->extra[2]); - } - break; - case java_util_zip_ZipFile_JZENTRY_COMMENT: - if (ze->comment != 0) { - len = (int)strlen(ze->comment); - if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) - break; - (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte*)ze->comment); - } - break; - } - return jba; -} - -JNIEXPORT jint JNICALL -Java_java_util_zip_ZipFile_read(JNIEnv *env, jclass cls, jlong zfile, - jlong zentry, jlong pos, jbyteArray bytes, - jint off, jint len) -{ - jzfile *zip = jlong_to_ptr(zfile); - char *msg; - -#define BUFSIZE 8192 - /* copy via tmp stack buffer: */ - jbyte buf[BUFSIZE]; - - if (len > BUFSIZE) { - len = BUFSIZE; - } - - ZIP_Lock(zip); - len = ZIP_Read(zip, jlong_to_ptr(zentry), pos, buf, len); - msg = zip->msg; - ZIP_Unlock(zip); - if (len != -1) { - (*env)->SetByteArrayRegion(env, bytes, off, len, buf); - } - - if (len == -1) { - if (msg != 0) { - ThrowZipException(env, msg); - } else { - char errmsg[128]; - sprintf(errmsg, "errno: %d, error: %s\n", - errno, "Error reading ZIP file"); - JNU_ThrowIOExceptionWithLastError(env, errmsg); - } - } - - return len; -} - -/* - * Returns an array of strings representing the names of all entries - * that begin with "META-INF/" (case ignored). This native method is - * used in JarFile as an optimization when looking up manifest and - * signature file entries. Returns null if no entries were found. - */ -JNIEXPORT jobjectArray JNICALL -Java_java_util_jar_JarFile_getMetaInfEntryNames(JNIEnv *env, jobject obj) -{ - jlong zfile = (*env)->GetLongField(env, obj, jzfileID); - jzfile *zip; - int i, count; - jobjectArray result = 0; - - if (zfile == 0) { - JNU_ThrowByName(env, - "java/lang/IllegalStateException", "zip file closed"); - return NULL; - } - zip = jlong_to_ptr(zfile); - - /* count the number of valid ZIP metanames */ - count = 0; - if (zip->metanames != 0) { - for (i = 0; i < zip->metacount; i++) { - if (zip->metanames[i] != 0) { - count++; - } - } - } - - /* If some names were found then build array of java strings */ - if (count > 0) { - jclass cls = JNU_ClassString(env); - CHECK_NULL_RETURN(cls, NULL); - result = (*env)->NewObjectArray(env, count, cls, 0); - CHECK_NULL_RETURN(result, NULL); - if (result != 0) { - for (i = 0; i < count; i++) { - jstring str = (*env)->NewStringUTF(env, zip->metanames[i]); - if (str == 0) { - break; - } - (*env)->SetObjectArrayElement(env, result, i, str); - (*env)->DeleteLocalRef(env, str); - } - } - } - return result; -} - -JNIEXPORT jstring JNICALL -Java_java_util_zip_ZipFile_getZipMessage(JNIEnv *env, jclass cls, jlong zfile) -{ - jzfile *zip = jlong_to_ptr(zfile); - char *msg = zip->msg; - if (msg == NULL) { - return NULL; - } - return JNU_NewStringPlatform(env, msg); -} diff --git a/jdk/test/java/util/zip/ZipFile/ReadZip.java b/jdk/test/java/util/zip/ZipFile/ReadZip.java index 1052642eda7..fe923e81eee 100644 --- a/jdk/test/java/util/zip/ZipFile/ReadZip.java +++ b/jdk/test/java/util/zip/ZipFile/ReadZip.java @@ -30,6 +30,7 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; +import java.nio.file.NoSuchFileException; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.zip.*; @@ -110,6 +111,6 @@ public class ReadZip { "input" + String.valueOf(new java.util.Random().nextInt()) + ".zip"))); - } catch (FileNotFoundException fnfe) {} + } catch (NoSuchFileException nsfe) {} } } diff --git a/jdk/test/java/util/zip/ZipFile/TestZipFile.java b/jdk/test/java/util/zip/ZipFile/TestZipFile.java new file mode 100644 index 00000000000..986877731db --- /dev/null +++ b/jdk/test/java/util/zip/ZipFile/TestZipFile.java @@ -0,0 +1,361 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8142508 + * @summary Tests various ZipFile apis + * @run main/manual TestZipFile + */ + +import java.io.*; +import java.lang.reflect.Method; +import java.nio.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.zip.*; + +public class TestZipFile { + + private static Random r = new Random(); + private static int N = 50; + private static int NN = 10; + private static int ENUM = 10000; + private static int ESZ = 10000; + private static ExecutorService executor = Executors.newFixedThreadPool(20); + private static Set paths = new HashSet<>(); + + static void realMain (String[] args) throws Throwable { + + try { + for (int i = 0; i < N; i++) { + test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); + test(r.nextInt(ENUM), r.nextInt(ESZ), true, true); + } + + for (int i = 0; i < NN; i++) { + test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), false, true); + test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), true, true); + testCachedDelete(); + testCachedOverwrite(); + //test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); + } + + test(70000, 1000, false, true); // > 65536 entry number; + testDelete(); // OPEN_DELETE + + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.MINUTES); + } finally { + for (Path path : paths) { + Files.deleteIfExists(path); + } + } + } + + static void test(int numEntry, int szMax, boolean addPrefix, boolean cleanOld) { + String name = "zftest" + r.nextInt() + ".zip"; + Zip zip = new Zip(name, numEntry, szMax, addPrefix, cleanOld); + for (int i = 0; i < NN; i++) { + executor.submit(() -> doTest(zip)); + } + } + + // test scenario: + // (1) open the ZipFile(zip) with OPEN_READ | OPEN_DELETE + // (2) test the ZipFile works correctly + // (3) check the zip is deleted after ZipFile gets closed + static void testDelete() throws Throwable { + String name = "zftest" + r.nextInt() + ".zip"; + Zip zip = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); + try (ZipFile zf = new ZipFile(new File(zip.name), + ZipFile.OPEN_READ | ZipFile.OPEN_DELETE )) + { + doTest0(zip, zf); + } + Path p = Paths.get(name); + if (Files.exists(p)) { + fail("Failed to delete " + name + " with OPEN_DELETE"); + } + } + + // test scenario: + // (1) keep a ZipFile(zip1) alive (in ZipFile's cache), dont close it + // (2) delete zip1 and create zip2 with the same name the zip1 with zip2 + // (3) zip1 tests should fail, but no crash + // (4) zip2 tasks should all get zip2, then pass normal testing. + static void testCachedDelete() throws Throwable { + String name = "zftest" + r.nextInt() + ".zip"; + Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); + + try (ZipFile zf = new ZipFile(zip1.name)) { + for (int i = 0; i < NN; i++) { + executor.submit(() -> verifyNoCrash(zip1)); + } + // delete the "zip1" and create a new one to test + Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); + /* + System.out.println("========================================"); + System.out.printf(" zip1=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", + zip1.name, zip1.lastModified, zip1.entries.size(), + zip1.attrs.fileKey(), zip1.attrs.size(), zip1.attrs.lastModifiedTime().toMillis()); + System.out.printf(" zip2=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", + zip2.name, zip2.lastModified, zip2.entries.size(), + zip2.attrs.fileKey(), zip2.attrs.size(), zip2.attrs.lastModifiedTime().toMillis()); + */ + for (int i = 0; i < NN; i++) { + executor.submit(() -> doTest(zip2)); + } + } + } + + // overwrite the "zip1" and create a new one to test. So the two zip files + // have the same fileKey, but probably different lastModified() + static void testCachedOverwrite() throws Throwable { + String name = "zftest" + r.nextInt() + ".zip"; + Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); + try (ZipFile zf = new ZipFile(zip1.name)) { + for (int i = 0; i < NN; i++) { + executor.submit(() -> verifyNoCrash(zip1)); + } + // overwrite the "zip1" with new contents + Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, false); + for (int i = 0; i < NN; i++) { + executor.submit(() -> doTest(zip2)); + } + } + } + + // just check the entries and contents. since the file has been either overwritten + // or deleted/rewritten, we only care if it crahes or not. + static void verifyNoCrash(Zip zip) throws RuntimeException { + try (ZipFile zf = new ZipFile(zip.name)) { + List zlist = new ArrayList(zip.entries.keySet()); + String[] elist = zf.stream().map( e -> e.getName()).toArray(String[]::new); + if (!Arrays.equals(elist, + zlist.stream().map( e -> e.getName()).toArray(String[]::new))) + { + //System.out.printf("++++++ LIST NG [%s] entries.len=%d, expected=%d+++++++%n", + // zf.getName(), elist.length, zlist.size()); + return; + } + for (ZipEntry ze : zlist) { + byte[] zdata = zip.entries.get(ze); + ZipEntry e = zf.getEntry(ze.getName()); + if (e != null) { + checkEqual(e, ze); + if (!e.isDirectory()) { + // check with readAllBytes + try (InputStream is = zf.getInputStream(e)) { + if (!Arrays.equals(zdata, is.readAllBytes())) { + //System.out.printf("++++++ BYTES NG [%s]/[%s] ++++++++%n", + // zf.getName(), ze.getName()); + } + } + } + } + } + } catch (Throwable t) { + // t.printStackTrace(); + // fail(t.toString()); + } + } + + static void checkEqual(ZipEntry x, ZipEntry y) { + if (x.getName().equals(y.getName()) && + x.isDirectory() == y.isDirectory() && + x.getMethod() == y.getMethod() && + (x.getTime() / 2000) == y.getTime() / 2000 && + x.getSize() == y.getSize() && + x.getCompressedSize() == y.getCompressedSize() && + x.getCrc() == y.getCrc() && + x.getComment().equals(y.getComment()) + ) { + pass(); + } else { + fail(x + " not equal to " + y); + System.out.printf(" %s %s%n", x.getName(), y.getName()); + System.out.printf(" %d %d%n", x.getMethod(), y.getMethod()); + System.out.printf(" %d %d%n", x.getTime(), y.getTime()); + System.out.printf(" %d %d%n", x.getSize(), y.getSize()); + System.out.printf(" %d %d%n", x.getCompressedSize(), y.getCompressedSize()); + System.out.printf(" %d %d%n", x.getCrc(), y.getCrc()); + System.out.println("-----------------"); + } + } + + static void doTest(Zip zip) throws RuntimeException { + //Thread me = Thread.currentThread(); + try (ZipFile zf = new ZipFile(zip.name)) { + doTest0(zip, zf); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + static void doTest0(Zip zip, ZipFile zf) throws Throwable { + List list = new ArrayList(zip.entries.keySet()); + // (1) check entry list, in expected order + if (!check(Arrays.equals( + list.stream().map( e -> e.getName()).toArray(String[]::new), + zf.stream().map( e -> e.getName()).toArray(String[]::new)))) { + return; + } + // (2) shuffle, and check each entry and its bytes + Collections.shuffle(list); + for (ZipEntry ze : list) { + byte[] data = zip.entries.get(ze); + ZipEntry e = zf.getEntry(ze.getName()); + checkEqual(e, ze); + if (!e.isDirectory()) { + // check with readAllBytes + try (InputStream is = zf.getInputStream(e)) { + check(Arrays.equals(data, is.readAllBytes())); + } + // check with smaller sized buf + try (InputStream is = zf.getInputStream(e)) { + byte[] buf = new byte[(int)e.getSize()]; + int sz = r.nextInt((int)e.getSize()/4 + 1) + 1; + int off = 0; + int n; + while ((n = is.read(buf, off, buf.length - off)) > 0) { + off += n; + } + check(is.read() == -1); + check(Arrays.equals(data, buf)); + } + } + } + // (3) check getMetaInfEntryNames + String[] metas = list.stream() + .map( e -> e.getName()) + .filter( s -> s.startsWith("META-INF/")) + .sorted() + .toArray(String[]::new); + if (metas.length > 0) { + // meta-inf entries + Method getMetas = ZipFile.class.getDeclaredMethod("getMetaInfEntryNames"); + getMetas.setAccessible(true); + String[] names = (String[])getMetas.invoke(zf); + if (names == null) { + fail("Failed to get metanames from " + zf); + } else { + Arrays.sort(names); + check(Arrays.equals(names, metas)); + } + } + } + + private static class Zip { + String name; + Map entries; + BasicFileAttributes attrs; + long lastModified; + + Zip(String name, int num, int szMax, boolean prefix, boolean clean) { + this.name = name; + entries = new LinkedHashMap<>(num); + try { + Path p = Paths.get(name); + if (clean) { + Files.deleteIfExists(p); + } + paths.add(p); + } catch (Exception x) { + throw (RuntimeException)x; + } + + try (FileOutputStream fos = new FileOutputStream(name); + BufferedOutputStream bos = new BufferedOutputStream(fos); + ZipOutputStream zos = new ZipOutputStream(bos)) + { + if (prefix) { + byte[] bytes = new byte[r.nextInt(1000)]; + r.nextBytes(bytes); + bos.write(bytes); + } + CRC32 crc = new CRC32(); + for (int i = 0; i < num; i++) { + String ename = "entry-" + i + "-name-" + r.nextLong(); + ZipEntry ze = new ZipEntry(ename); + int method = r.nextBoolean() ? ZipEntry.STORED : ZipEntry.DEFLATED; + writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); + } + // add some manifest entries + for (int i = 0; i < r.nextInt(20); i++) { + String meta = "META-INF/" + "entry-" + i + "-metainf-" + r.nextLong(); + ZipEntry ze = new ZipEntry(meta); + writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); + } + } catch (Exception x) { + throw (RuntimeException)x; + } + try { + this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class); + this.lastModified = new File(name).lastModified(); + } catch (Exception x) { + throw (RuntimeException)x; + } + } + + private void writeEntry(ZipOutputStream zos, CRC32 crc, + ZipEntry ze, int method, int szMax) + throws IOException + { + ze.setMethod(method); + byte[] data = new byte[r.nextInt(szMax + 1)]; + r.nextBytes(data); + if (method == ZipEntry.STORED) { // must set size/csize/crc + ze.setSize(data.length); + ze.setCompressedSize(data.length); + crc.reset(); + crc.update(data); + ze.setCrc(crc.getValue()); + } + ze.setTime(System.currentTimeMillis()); + ze.setComment(ze.getName()); + zos.putNextEntry(ze); + zos.write(data); + zos.closeEntry(); + entries.put(ze, data); + } + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static void pass() {passed++;} + static void pass(String msg) {System.out.println(msg); passed++;} + static void fail() {failed++; Thread.dumpStack();} + static void fail(String msg) {System.out.println(msg); fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static void unexpected(Throwable t, String msg) { + System.out.println(msg); failed++; t.printStackTrace();} + static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} + + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.println("\nPassed = " + passed + " failed = " + failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} From b0e22f96036337799a90954d9c5eb98d5ffed0e6 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Tue, 8 Dec 2015 16:43:58 -0800 Subject: [PATCH 014/199] 8144958: changes by JDK-8142508 seems to have broken jtreg Reviewed-by: darcy --- jdk/make/mapfiles/libzip/mapfile-vers | 22 +- jdk/make/mapfiles/libzip/reorder-sparc | 16 + jdk/make/mapfiles/libzip/reorder-sparcv9 | 15 + jdk/make/mapfiles/libzip/reorder-x86 | 18 + .../share/classes/java/util/jar/JarFile.java | 5 +- .../share/classes/java/util/zip/ZipCoder.java | 21 +- .../share/classes/java/util/zip/ZipFile.java | 837 ++++-------------- .../share/classes/java/util/zip/ZipUtils.java | 79 +- .../internal/misc/JavaUtilZipFileAccess.java | 1 - .../java.base/share/classes/sun/misc/VM.java | 3 + .../java.base/share/native/libzip/ZipFile.c | 406 +++++++++ jdk/test/java/util/zip/ZipFile/ReadZip.java | 3 +- .../java/util/zip/ZipFile/TestZipFile.java | 361 -------- 13 files changed, 672 insertions(+), 1115 deletions(-) create mode 100644 jdk/src/java.base/share/native/libzip/ZipFile.c delete mode 100644 jdk/test/java/util/zip/ZipFile/TestZipFile.java diff --git a/jdk/make/mapfiles/libzip/mapfile-vers b/jdk/make/mapfiles/libzip/mapfile-vers index c1ab48c10cf..ceace23f26d 100644 --- a/jdk/make/mapfiles/libzip/mapfile-vers +++ b/jdk/make/mapfiles/libzip/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2013, 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 @@ -27,6 +27,7 @@ SUNWprivate_1.1 { global: + Java_java_util_jar_JarFile_getMetaInfEntryNames; Java_java_util_zip_Adler32_update; Java_java_util_zip_Adler32_updateBytes; Java_java_util_zip_Adler32_updateByteBuffer; @@ -47,6 +48,25 @@ SUNWprivate_1.1 { Java_java_util_zip_Inflater_initIDs; Java_java_util_zip_Inflater_reset; Java_java_util_zip_Inflater_setDictionary; + Java_java_util_zip_ZipFile_close; + Java_java_util_zip_ZipFile_getCommentBytes; + Java_java_util_zip_ZipFile_freeEntry; + Java_java_util_zip_ZipFile_getEntry; + Java_java_util_zip_ZipFile_getEntryBytes; + Java_java_util_zip_ZipFile_getEntryCrc; + Java_java_util_zip_ZipFile_getEntryCSize; + Java_java_util_zip_ZipFile_getEntryFlag; + Java_java_util_zip_ZipFile_getEntryMethod; + Java_java_util_zip_ZipFile_getEntrySize; + Java_java_util_zip_ZipFile_getEntryTime; + Java_java_util_zip_ZipFile_getNextEntry; + Java_java_util_zip_ZipFile_getZipMessage; + Java_java_util_zip_ZipFile_getTotal; + Java_java_util_zip_ZipFile_initIDs; + Java_java_util_zip_ZipFile_open; + Java_java_util_zip_ZipFile_read; + Java_java_util_zip_ZipFile_startsWithLOC; + ZIP_Close; ZIP_CRC32; ZIP_FindEntry; diff --git a/jdk/make/mapfiles/libzip/reorder-sparc b/jdk/make/mapfiles/libzip/reorder-sparc index 63b2ad6fc28..154e7998a53 100644 --- a/jdk/make/mapfiles/libzip/reorder-sparc +++ b/jdk/make/mapfiles/libzip/reorder-sparc @@ -16,14 +16,30 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; +text: .text%Java_java_util_zip_ZipFile_initIDs; +text: .text%Java_java_util_zip_ZipFile_open; +text: .text%Java_java_util_zip_ZipFile_getTotal; +text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +text: .text%Java_java_util_zip_ZipFile_getEntry; +text: .text%Java_java_util_zip_ZipFile_freeEntry; +text: .text%Java_java_util_zip_ZipFile_getEntryTime; +text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +text: .text%Java_java_util_zip_ZipFile_getEntrySize; +text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +text: .text%Java_java_util_zip_ZipFile_getEntryBytes; text: .text%Java_java_util_zip_Inflater_initIDs; text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; text: .text%zcalloc; text: .text%Java_java_util_zip_Inflater_inflateBytes; +text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; +text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; text: .text%Java_java_util_zip_Inflater_reset; text: .text%Java_java_util_zip_Inflater_end; text: .text%inflateEnd; +text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; diff --git a/jdk/make/mapfiles/libzip/reorder-sparcv9 b/jdk/make/mapfiles/libzip/reorder-sparcv9 index caca118de98..89690b8dabb 100644 --- a/jdk/make/mapfiles/libzip/reorder-sparcv9 +++ b/jdk/make/mapfiles/libzip/reorder-sparcv9 @@ -15,6 +15,19 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; +text: .text%Java_java_util_zip_ZipFile_initIDs; +text: .text%Java_java_util_zip_ZipFile_open; +text: .text%Java_java_util_zip_ZipFile_getTotal; +text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +text: .text%Java_java_util_zip_ZipFile_getEntry; +text: .text%Java_java_util_zip_ZipFile_freeEntry; +text: .text%Java_java_util_zip_ZipFile_getEntryTime; +text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +text: .text%Java_java_util_zip_ZipFile_getEntrySize; +text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +text: .text%Java_java_util_zip_ZipFile_getEntryBytes; text: .text%Java_java_util_zip_Inflater_initIDs; text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; @@ -22,6 +35,7 @@ text: .text%zcalloc; text: .text%inflateReset; text: .text%Java_java_util_zip_Inflater_inflateBytes; text: .text%inflate; +text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; @@ -29,5 +43,6 @@ text: .text%ZIP_ReadEntry; text: .text%InflateFully; text: .text%inflateEnd; text: .text%Java_java_util_zip_Inflater_reset; +text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; text: .text%Java_java_util_zip_Inflater_end; diff --git a/jdk/make/mapfiles/libzip/reorder-x86 b/jdk/make/mapfiles/libzip/reorder-x86 index dfd57c7752e..746948315eb 100644 --- a/jdk/make/mapfiles/libzip/reorder-x86 +++ b/jdk/make/mapfiles/libzip/reorder-x86 @@ -16,16 +16,34 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; +text: .text%Java_java_util_zip_ZipFile_initIDs; +text: .text%Java_java_util_zip_ZipFile_open; +text: .text%Java_java_util_zip_ZipFile_getTotal; +text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +text: .text%Java_java_util_zip_ZipFile_getEntry; +text: .text%Java_java_util_zip_ZipFile_freeEntry; +text: .text%Java_java_util_zip_ZipFile_getEntryTime; +text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +text: .text%Java_java_util_zip_ZipFile_getEntrySize; +text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +text: .text%Java_java_util_zip_ZipFile_getEntryBytes; +text: .text%Java_java_util_zip_Inflater_initIDs; +text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; text: .text%zcalloc; text: .text%inflateReset; text: .text%Java_java_util_zip_Inflater_inflateBytes; text: .text%inflate; +text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; +text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; text: .text%ZIP_ReadEntry; text: .text%InflateFully; text: .text%inflateEnd; text: .text%Java_java_util_zip_Inflater_reset; +text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; text: .text%Java_java_util_zip_Inflater_end; diff --git a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java index 62734ceefbd..6d16a517ac9 100644 --- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java @@ -203,10 +203,7 @@ class JarFile extends ZipFile { return man; } - private String[] getMetaInfEntryNames() { - return jdk.internal.misc.SharedSecrets.getJavaUtilZipFileAccess() - .getMetaInfEntryNames((ZipFile)this); - } + private native String[] getMetaInfEntryNames(); /** * Returns the {@code JarEntry} for the given entry name or diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java b/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java index 243d6e8c065..b920b820e03 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -43,7 +43,7 @@ import sun.nio.cs.ArrayEncoder; final class ZipCoder { - String toString(byte[] ba, int off, int length) { + String toString(byte[] ba, int length) { CharsetDecoder cd = decoder().reset(); int len = (int)(length * cd.maxCharsPerByte()); char[] ca = new char[len]; @@ -53,12 +53,12 @@ final class ZipCoder { // CodingErrorAction.REPLACE mode. ZipCoder uses // REPORT mode. if (isUTF8 && cd instanceof ArrayDecoder) { - int clen = ((ArrayDecoder)cd).decode(ba, off, length, ca); + int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca); if (clen == -1) // malformed throw new IllegalArgumentException("MALFORMED"); return new String(ca, 0, clen); } - ByteBuffer bb = ByteBuffer.wrap(ba, off, length); + ByteBuffer bb = ByteBuffer.wrap(ba, 0, length); CharBuffer cb = CharBuffer.wrap(ca); CoderResult cr = cd.decode(bb, cb, true); if (!cr.isUnderflow()) @@ -69,12 +69,8 @@ final class ZipCoder { return new String(ca, 0, cb.position()); } - String toString(byte[] ba, int length) { - return toString(ba, 0, length); - } - String toString(byte[] ba) { - return toString(ba, 0, ba.length); + return toString(ba, ba.length); } byte[] getBytes(String s) { @@ -115,16 +111,13 @@ final class ZipCoder { return utf8.getBytes(s); } - String toStringUTF8(byte[] ba, int len) { - return toStringUTF8(ba, 0, len); - } - String toStringUTF8(byte[] ba, int off, int len) { + String toStringUTF8(byte[] ba, int len) { if (isUTF8) - return toString(ba, off, len); + return toString(ba, len); if (utf8 == null) utf8 = new ZipCoder(StandardCharsets.UTF_8); - return utf8.toString(ba, off, len); + return utf8.toString(ba, len); } boolean isUTF8() { diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java index 561023c30e7..4e3a6d20417 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -30,22 +30,14 @@ import java.io.InputStream; import java.io.IOException; import java.io.EOFException; import java.io.File; -import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.Path; -import java.nio.file.Files; - import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Deque; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Objects; import java.util.NoSuchElementException; import java.util.Spliterator; import java.util.Spliterators; @@ -55,9 +47,7 @@ import java.util.stream.StreamSupport; import jdk.internal.misc.JavaUtilZipFileAccess; import jdk.internal.misc.SharedSecrets; -import static java.util.zip.ZipConstants.*; import static java.util.zip.ZipConstants64.*; -import static java.util.zip.ZipUtils.*; /** * This class is used to read entries from a zip file. @@ -70,11 +60,11 @@ import static java.util.zip.ZipUtils.*; */ public class ZipFile implements ZipConstants, Closeable { - + private long jzfile; // address of jzfile data private final String name; // zip file name + private final int total; // total number of entries + private final boolean locsig; // if zip file starts with LOCSIG (usually true) private volatile boolean closeRequested = false; - private Source zsrc; - private ZipCoder zc; private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; @@ -93,6 +83,23 @@ class ZipFile implements ZipConstants, Closeable { */ public static final int OPEN_DELETE = 0x4; + static { + /* Zip library is loaded from System.initializeSystemClass */ + initIDs(); + } + + private static native void initIDs(); + + private static final boolean usemmap; + + static { + // A system prpperty to disable mmap use to avoid vm crash when + // in-use zip file is accidently overwritten by others. + String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping"); + usemmap = (prop == null || + !(prop.length() == 0 || prop.equalsIgnoreCase("true"))); + } + /** * Opens a zip file for reading. * @@ -158,6 +165,8 @@ class ZipFile implements ZipConstants, Closeable { this(file, OPEN_READ); } + private ZipCoder zc; + /** * Opens a new {@code ZipFile} to read from the specified * {@code File} object in the specified mode. The mode argument @@ -205,13 +214,16 @@ class ZipFile implements ZipConstants, Closeable { sm.checkDelete(name); } } - Objects.requireNonNull(charset, "charset"); + if (charset == null) + throw new NullPointerException("charset is null"); this.zc = ZipCoder.get(charset); - this.name = name; long t0 = System.nanoTime(); - this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0); + jzfile = open(name, mode, file.lastModified(), usemmap); sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0); sun.misc.PerfCounter.getZipFileCount().increment(); + this.name = name; + this.total = getTotal(jzfile); + this.locsig = startsWithLOC(jzfile); } /** @@ -245,7 +257,6 @@ class ZipFile implements ZipConstants, Closeable { /** * Opens a ZIP file for reading given the specified File object. - * * @param file the ZIP file to be opened for reading * @param charset * The {@linkplain java.nio.charset.Charset charset} to be @@ -276,10 +287,10 @@ class ZipFile implements ZipConstants, Closeable { public String getComment() { synchronized (this) { ensureOpen(); - if (zsrc.comment == null) { + byte[] bcomm = getCommentBytes(jzfile); + if (bcomm == null) return null; - } - return zc.toString(zsrc.comment); + return zc.toString(bcomm, bcomm.length); } } @@ -292,27 +303,38 @@ class ZipFile implements ZipConstants, Closeable { * @throws IllegalStateException if the zip file has been closed */ public ZipEntry getEntry(String name) { - Objects.requireNonNull(name, "name"); + if (name == null) { + throw new NullPointerException("name"); + } + long jzentry = 0; synchronized (this) { ensureOpen(); - int pos = zsrc.getEntryPos(zc.getBytes(name), true); - if (pos != -1) { - return getZipEntry(name, pos); + jzentry = getEntry(jzfile, zc.getBytes(name), true); + if (jzentry != 0) { + ZipEntry ze = getZipEntry(name, jzentry); + freeEntry(jzfile, jzentry); + return ze; } } return null; } - // The outstanding inputstreams that need to be closed, + private static native long getEntry(long jzfile, byte[] name, + boolean addSlash); + + // freeEntry releases the C jzentry struct. + private static native void freeEntry(long jzfile, long jzentry); + + // the outstanding inputstreams that need to be closed, // mapped to the inflater objects they use. private final Map streams = new WeakHashMap<>(); /** * Returns an input stream for reading the contents of the specified * zip file entry. - *

- * Closing this ZIP file will, in turn, close all input streams that - * have been returned by invocations of this method. + * + *

Closing this ZIP file will, in turn, close all input + * streams that have been returned by invocations of this method. * * @param entry the zip file entry * @return the input stream for reading the contents of the specified @@ -322,38 +344,37 @@ class ZipFile implements ZipConstants, Closeable { * @throws IllegalStateException if the zip file has been closed */ public InputStream getInputStream(ZipEntry entry) throws IOException { - Objects.requireNonNull(entry, "entry"); - int pos = -1; + if (entry == null) { + throw new NullPointerException("entry"); + } + long jzentry = 0; ZipFileInputStream in = null; synchronized (this) { ensureOpen(); if (!zc.isUTF8() && (entry.flag & EFS) != 0) { - pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false); + jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); } else { - pos = zsrc.getEntryPos(zc.getBytes(entry.name), false); + jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); } - if (pos == -1) { + if (jzentry == 0) { return null; } - in = new ZipFileInputStream(zsrc.cen, pos); - switch (CENHOW(zsrc.cen, pos)) { + in = new ZipFileInputStream(jzentry); + + switch (getEntryMethod(jzentry)) { case STORED: synchronized (streams) { streams.put(in, null); } return in; case DEFLATED: - // Inflater likes a bit of slack // MORE: Compute good size for inflater stream: - long size = CENLEN(zsrc.cen, pos) + 2; - if (size > 65536) { - size = 8192; - } - if (size <= 0) { - size = 4096; - } + long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack + if (size > 65536) size = 8192; + if (size <= 0) size = 4096; Inflater inf = getInflater(); - InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size); + InputStream is = + new ZipFileInflaterInputStream(in, inf, (int)size); synchronized (streams) { streams.put(is, inf); } @@ -426,8 +447,8 @@ class ZipFile implements ZipConstants, Closeable { private Inflater getInflater() { Inflater inf; synchronized (inflaterCache) { - while ((inf = inflaterCache.poll()) != null) { - if (!inf.ended()) { + while (null != (inf = inflaterCache.poll())) { + if (false == inf.ended()) { return inf; } } @@ -439,7 +460,7 @@ class ZipFile implements ZipConstants, Closeable { * Releases the specified inflater to the list of available inflaters. */ private void releaseInflater(Inflater inf) { - if (!inf.ended()) { + if (false == inf.ended()) { inf.reset(); synchronized (inflaterCache) { inflaterCache.add(inf); @@ -448,7 +469,7 @@ class ZipFile implements ZipConstants, Closeable { } // List of available Inflater objects for decompression - private final Deque inflaterCache = new ArrayDeque<>(); + private Deque inflaterCache = new ArrayDeque<>(); /** * Returns the path name of the ZIP file. @@ -472,7 +493,7 @@ class ZipFile implements ZipConstants, Closeable { public boolean hasNext() { synchronized (ZipFile.this) { ensureOpen(); - return i < zsrc.total; + return i < total; } } @@ -483,11 +504,28 @@ class ZipFile implements ZipConstants, Closeable { public ZipEntry next() { synchronized (ZipFile.this) { ensureOpen(); - if (i >= zsrc.total) { + if (i >= total) { throw new NoSuchElementException(); } - // each "entry" has 3 ints in table entries - return getZipEntry(null, zsrc.getEntryPos(i++ * 3)); + long jzentry = getNextEntry(jzfile, i++); + if (jzentry == 0) { + String message; + if (closeRequested) { + message = "ZipFile concurrently closed"; + } else { + message = getZipMessage(ZipFile.this.jzfile); + } + throw new ZipError("jzentry == 0" + + ",\n jzfile = " + ZipFile.this.jzfile + + ",\n total = " + ZipFile.this.total + + ",\n name = " + ZipFile.this.name + + ",\n i = " + i + + ",\n message = " + message + ); + } + ZipEntry ze = getZipEntry(null, jzentry); + freeEntry(jzfile, jzentry); + return ze; } } @@ -521,53 +559,48 @@ class ZipFile implements ZipConstants, Closeable { Spliterator.IMMUTABLE | Spliterator.NONNULL), false); } - /* Checks ensureOpen() before invoke this method */ - private ZipEntry getZipEntry(String name, int pos) { - byte[] cen = zsrc.cen; + private ZipEntry getZipEntry(String name, long jzentry) { ZipEntry e = new ZipEntry(); - int nlen = CENNAM(cen, pos); - int elen = CENEXT(cen, pos); - int clen = CENCOM(cen, pos); - e.flag = CENFLG(cen, pos); // get the flag first + e.flag = getEntryFlag(jzentry); // get the flag first if (name != null) { e.name = name; } else { + byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME); if (!zc.isUTF8() && (e.flag & EFS) != 0) { - e.name = zc.toStringUTF8(cen, pos + CENHDR, nlen); + e.name = zc.toStringUTF8(bname, bname.length); } else { - e.name = zc.toString(cen, pos + CENHDR, nlen); + e.name = zc.toString(bname, bname.length); } } - e.xdostime = CENTIM(cen, pos); - e.crc = CENCRC(cen, pos); - e.size = CENLEN(cen, pos); - e.csize = CENSIZ(cen, pos); - e.method = CENHOW(cen, pos); - if (elen != 0) { - e.setExtra0(Arrays.copyOfRange(cen, pos + CENHDR + nlen, - pos + CENHDR + nlen + elen), true); - } - if (clen != 0) { + e.xdostime = getEntryTime(jzentry); + e.crc = getEntryCrc(jzentry); + e.size = getEntrySize(jzentry); + e.csize = getEntryCSize(jzentry); + e.method = getEntryMethod(jzentry); + e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false); + byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT); + if (bcomm == null) { + e.comment = null; + } else { if (!zc.isUTF8() && (e.flag & EFS) != 0) { - e.comment = zc.toStringUTF8(cen, pos + CENHDR + nlen + elen, clen); + e.comment = zc.toStringUTF8(bcomm, bcomm.length); } else { - e.comment = zc.toString(cen, pos + CENHDR + nlen + elen, clen); + e.comment = zc.toString(bcomm, bcomm.length); } } return e; } + private static native long getNextEntry(long jzfile, int i); + /** * Returns the number of entries in the ZIP file. - * * @return the number of entries in the ZIP file * @throws IllegalStateException if the zip file has been closed */ public int size() { - synchronized (this) { - ensureOpen(); - return zsrc.total; - } + ensureOpen(); + return total; } /** @@ -579,15 +612,14 @@ class ZipFile implements ZipConstants, Closeable { * @throws IOException if an I/O error has occurred */ public void close() throws IOException { - if (closeRequested) { + if (closeRequested) return; - } closeRequested = true; synchronized (this) { // Close streams, release their inflaters synchronized (streams) { - if (!streams.isEmpty()) { + if (false == streams.isEmpty()) { Map copy = new HashMap<>(streams); streams.clear(); for (Map.Entry e : copy.entrySet()) { @@ -599,17 +631,21 @@ class ZipFile implements ZipConstants, Closeable { } } } + // Release cached inflaters + Inflater inf; synchronized (inflaterCache) { - Inflater inf; - while ((inf = inflaterCache.poll()) != null) { + while (null != (inf = inflaterCache.poll())) { inf.end(); } } - // Release zip src - if (zsrc != null) { - Source.close(zsrc); - zsrc = null; + + if (jzfile != 0) { + // Close the zip file + long zf = this.jzfile; + jzfile = 0; + + close(zf); } } } @@ -632,11 +668,14 @@ class ZipFile implements ZipConstants, Closeable { close(); } + private static native void close(long jzfile); + private void ensureOpen() { if (closeRequested) { throw new IllegalStateException("zip file closed"); } - if (zsrc == null) { + + if (jzfile == 0) { throw new IllegalStateException("The object is not initialized."); } } @@ -652,86 +691,23 @@ class ZipFile implements ZipConstants, Closeable { * (possibly compressed) zip file entry. */ private class ZipFileInputStream extends InputStream { - private volatile boolean closeRequested = false; + private volatile boolean zfisCloseRequested = false; + protected long jzentry; // address of jzentry data private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry - ZipFileInputStream(byte[] cen, int cenpos) throws IOException { - rem = CENSIZ(cen, cenpos); - size = CENLEN(cen, cenpos); - pos = CENOFF(cen, cenpos); - // zip64 - if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL || - pos == ZIP64_MAGICVAL) { - checkZIP64(cen, cenpos); - } - // negative for lazy initialization, see getDataOffset(); - pos = - (pos + ZipFile.this.zsrc.locpos); - } - - private void checkZIP64(byte[] cen, int cenpos) throws IOException { - int off = cenpos + CENHDR + CENNAM(cen, cenpos); - int end = off + CENEXT(cen, cenpos); - while (off + 4 < end) { - int tag = get16(cen, off); - int sz = get16(cen, off + 2); - off += 4; - if (off + sz > end) // invalid data - break; - if (tag == EXTID_ZIP64) { - if (size == ZIP64_MAGICVAL) { - if (sz < 8 || (off + 8) > end) - break; - size = get64(cen, off); - sz -= 8; - off += 8; - } - if (rem == ZIP64_MAGICVAL) { - if (sz < 8 || (off + 8) > end) - break; - rem = get64(cen, off); - sz -= 8; - off += 8; - } - if (pos == ZIP64_MAGICVAL) { - if (sz < 8 || (off + 8) > end) - break; - pos = get64(cen, off); - sz -= 8; - off += 8; - } - break; - } - off += sz; - } - } - - /* The Zip file spec explicitly allows the LOC extra data size to - * be different from the CEN extra data size. Since we cannot trust - * the CEN extra data size, we need to read the LOC to determine - * the entry data offset. - */ - private long initDataOffset() throws IOException { - if (pos <= 0) { - byte[] loc = new byte[LOCHDR]; - pos = -pos; - int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos); - if (len != LOCHDR) { - throw new ZipException("ZipFile error reading zip file"); - } - if (LOCSIG(loc) != LOCSIG) { - throw new ZipException("ZipFile invalid LOC header (bad signature)"); - } - pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc); - } - return pos; + ZipFileInputStream(long jzentry) { + pos = 0; + rem = getEntryCSize(jzentry); + size = getEntrySize(jzentry); + this.jzentry = jzentry; } public int read(byte b[], int off, int len) throws IOException { synchronized (ZipFile.this) { - ensureOpenOrZipException(); - initDataOffset(); + long rem = this.rem; + long pos = this.pos; if (rem == 0) { return -1; } @@ -741,10 +717,14 @@ class ZipFile implements ZipConstants, Closeable { if (len > rem) { len = (int) rem; } - len = ZipFile.this.zsrc.readAt(b, off, len, pos); + + // Check if ZipFile open + ensureOpenOrZipException(); + len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, + off, len); if (len > 0) { - pos += len; - rem -= len; + this.pos = (pos + len); + this.rem = (rem - len); } } if (rem == 0) { @@ -762,16 +742,11 @@ class ZipFile implements ZipConstants, Closeable { } } - public long skip(long n) throws IOException { - synchronized (ZipFile.this) { - ensureOpenOrZipException(); - initDataOffset(); - if (n > rem) { - n = rem; - } - pos += n; - rem -= n; - } + public long skip(long n) { + if (n > rem) + n = rem; + pos += n; + rem -= n; if (rem == 0) { close(); } @@ -787,11 +762,17 @@ class ZipFile implements ZipConstants, Closeable { } public void close() { - if (closeRequested) { + if (zfisCloseRequested) return; - } - closeRequested = true; + zfisCloseRequested = true; + rem = 0; + synchronized (ZipFile.this) { + if (jzentry != 0 && ZipFile.this.jzfile != 0) { + freeEntry(ZipFile.this.jzfile, jzentry); + jzentry = 0; + } + } synchronized (streams) { streams.remove(this); } @@ -806,492 +787,40 @@ class ZipFile implements ZipConstants, Closeable { SharedSecrets.setJavaUtilZipFileAccess( new JavaUtilZipFileAccess() { public boolean startsWithLocHeader(ZipFile zip) { - return zip.zsrc.locsig; + return zip.startsWithLocHeader(); } - public String[] getMetaInfEntryNames(ZipFile zip) { - return zip.getMetaInfEntryNames(); - } - } + } ); } - /* - * Returns an array of strings representing the names of all entries - * that begin with "META-INF/" (case ignored). This method is used - * in JarFile, via SharedSecrets, as an optimization when looking up - * manifest and signature file entries. Returns null if no entries - * were found. + /** + * Returns {@code true} if, and only if, the zip file begins with {@code + * LOCSIG}. */ - private String[] getMetaInfEntryNames() { - synchronized (this) { - ensureOpen(); - if (zsrc.metanames.size() == 0) { - return null; - } - String[] names = new String[zsrc.metanames.size()]; - byte[] cen = zsrc.cen; - for (int i = 0; i < names.length; i++) { - int pos = zsrc.metanames.get(i); - names[i] = zc.toStringUTF8(cen, pos + CENHDR, CENNAM(cen, pos)); - } - return names; - } + private boolean startsWithLocHeader() { + return locsig; } - private static class Source { - private final Key key; // the key in files - private int refs = 1; + private static native long open(String name, int mode, long lastModified, + boolean usemmap) throws IOException; + private static native int getTotal(long jzfile); + private static native boolean startsWithLOC(long jzfile); + private static native int read(long jzfile, long jzentry, + long pos, byte[] b, int off, int len); - private RandomAccessFile zfile; // zfile of the underlying zip file - private byte[] cen; // CEN & ENDHDR - private long locpos; // position of first LOC header (usually 0) - private byte[] comment; // zip file comment - // list of meta entries in META-INF dir - private ArrayList metanames = new ArrayList<>(); - private final boolean locsig; // true, if zip file starts with LOCSIG (usually true) + // access to the native zentry object + private static native long getEntryTime(long jzentry); + private static native long getEntryCrc(long jzentry); + private static native long getEntryCSize(long jzentry); + private static native long getEntrySize(long jzentry); + private static native int getEntryMethod(long jzentry); + private static native int getEntryFlag(long jzentry); + private static native byte[] getCommentBytes(long jzfile); - // A Hashmap for all entries. - // - // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR, - // We might have a lot of these in a typical system. In order to save space we don't - // keep the name in memory, but merely remember a 32 bit {@code hash} value of the - // entry name and its offset {@code pos} in the central directory hdeader. - // - // private static class Entry { - // int hash; // 32 bit hashcode on name - // int next; // hash chain: index into entries - // int pos; // Offset of central directory file header - // } - // private Entry[] entries; // array of hashed cen entry - // - // To reduce the total size of entries further, we use a int[] here to store 3 "int" - // {@code hash}, {@code next and {@code "pos for each entry. The entry can then be - // referred by their index of their positions in the {@code entries}. - // - private int[] entries; // array of hashed cen entry - private int addEntry(int index, int hash, int next, int pos) { - entries[index++] = hash; - entries[index++] = next; - entries[index++] = pos; - return index; - } - private int getEntryHash(int index) { return entries[index]; } - private int getEntryNext(int index) { return entries[index + 1]; } - private int getEntryPos(int index) { return entries[index + 2]; } - private static final int ZIP_ENDCHAIN = -1; - private int total; // total number of entries - private int[] table; // Hash chain heads: indexes into entries - private int tablelen; // number of hash heads + private static final int JZENTRY_NAME = 0; + private static final int JZENTRY_EXTRA = 1; + private static final int JZENTRY_COMMENT = 2; + private static native byte[] getEntryBytes(long jzentry, int type); - private static class Key { - BasicFileAttributes attrs; - File file; - - public Key(File file, BasicFileAttributes attrs) { - this.attrs = attrs; - this.file = file; - } - - public int hashCode() { - long t = attrs.lastModifiedTime().toMillis(); - return ((int)(t ^ (t >>> 32))) + file.hashCode(); - } - - public boolean equals(Object obj) { - if (obj instanceof Key) { - Key key = (Key)obj; - if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) { - return false; - } - Object fk = attrs.fileKey(); - if (fk != null) { - return fk.equals(key.attrs.fileKey()); - } else { - return file.equals(key.file); - } - } - return false; - } - } - private static final HashMap files = new HashMap<>(); - - public static Source get(File file, boolean toDelete) throws IOException { - Key key = new Key(file, - Files.readAttributes(file.toPath(), BasicFileAttributes.class)); - Source src = null; - synchronized (files) { - src = files.get(key); - if (src != null) { - src.refs++; - return src; - } - } - src = new Source(key, toDelete); - synchronized (files) { - if (files.containsKey(key)) { // someone else put in first - src.close(); // close the newly created one - src = files.get(key); - src.refs++; - return src; - } - files.put(key, src); - return src; - } - } - - private static void close(Source src) throws IOException { - synchronized (files) { - if (--src.refs == 0) { - files.remove(src.key); - src.close(); - } - } - } - - private Source(Key key, boolean toDelete) throws IOException { - this.key = key; - this.zfile = new RandomAccessFile(key.file, "r"); - if (toDelete) { - key.file.delete(); - } - initCEN(-1); - byte[] buf = new byte[4]; - readFullyAt(buf, 0, 4, 0); - this.locsig = (LOCSIG(buf) != LOCSIG); - } - - private void close() throws IOException { - zfile.close(); - zfile = null; - cen = null; - entries = null; - table = null; - metanames = null; - } - - private static final int BUF_SIZE = 8192; - private final int readFullyAt(byte[] buf, int off, int len, long pos) - throws IOException - { - synchronized(zfile) { - zfile.seek(pos); - int N = len; - while (N > 0) { - int n = Math.min(BUF_SIZE, N); - zfile.readFully(buf, off, n); - off += n; - N -= n; - } - return len; - } - } - - private final int readAt(byte[] buf, int off, int len, long pos) - throws IOException - { - synchronized(zfile) { - zfile.seek(pos); - return zfile.read(buf, off, len); - } - } - - private static final int hashN(byte[] a, int off, int len) { - int h = 1; - while (len-- > 0) { - h = 31 * h + a[off++]; - } - return h; - } - - private static final int hash_append(int hash, byte b) { - return hash * 31 + b; - } - - private static class End { - int centot; // 4 bytes - long cenlen; // 4 bytes - long cenoff; // 4 bytes - long endpos; // 4 bytes - } - - /* - * Searches for end of central directory (END) header. The contents of - * the END header will be read and placed in endbuf. Returns the file - * position of the END header, otherwise returns -1 if the END header - * was not found or an error occurred. - */ - private End findEND() throws IOException { - long ziplen = zfile.length(); - if (ziplen <= 0) - zerror("zip file is empty"); - End end = new End(); - byte[] buf = new byte[READBLOCKSZ]; - long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0; - long minPos = minHDR - (buf.length - ENDHDR); - for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) { - int off = 0; - if (pos < 0) { - // Pretend there are some NUL bytes before start of file - off = (int)-pos; - Arrays.fill(buf, 0, off, (byte)0); - } - int len = buf.length - off; - if (readFullyAt(buf, off, len, pos + off) != len ) { - zerror("zip END header not found"); - } - // Now scan the block backwards for END header signature - for (int i = buf.length - ENDHDR; i >= 0; i--) { - if (buf[i+0] == (byte)'P' && - buf[i+1] == (byte)'K' && - buf[i+2] == (byte)'\005' && - buf[i+3] == (byte)'\006') { - // Found ENDSIG header - byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR); - end.centot = ENDTOT(endbuf); - end.cenlen = ENDSIZ(endbuf); - end.cenoff = ENDOFF(endbuf); - end.endpos = pos + i; - int comlen = ENDCOM(endbuf); - if (end.endpos + ENDHDR + comlen != ziplen) { - // ENDSIG matched, however the size of file comment in it does - // not match the real size. One "common" cause for this problem - // is some "extra" bytes are padded at the end of the zipfile. - // Let's do some extra verification, we don't care about the - // performance in this situation. - byte[] sbuf = new byte[4]; - long cenpos = end.endpos - end.cenlen; - long locpos = cenpos - end.cenoff; - if (cenpos < 0 || - locpos < 0 || - readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 || - GETSIG(sbuf) != CENSIG || - readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 || - GETSIG(sbuf) != LOCSIG) { - continue; - } - } - if (comlen > 0) { // this zip file has comlen - comment = new byte[comlen]; - if (readFullyAt(comment, 0, comlen, end.endpos + ENDHDR) != comlen) { - zerror("zip comment read failed"); - } - } - if (end.cenlen == ZIP64_MAGICVAL || - end.cenoff == ZIP64_MAGICVAL || - end.centot == ZIP64_MAGICCOUNT) - { - // need to find the zip64 end; - try { - byte[] loc64 = new byte[ZIP64_LOCHDR]; - if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) - != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) { - return end; - } - long end64pos = ZIP64_LOCOFF(loc64); - byte[] end64buf = new byte[ZIP64_ENDHDR]; - if (readFullyAt(end64buf, 0, end64buf.length, end64pos) - != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) { - return end; - } - // end64 found, re-calcualte everything. - end.cenlen = ZIP64_ENDSIZ(end64buf); - end.cenoff = ZIP64_ENDOFF(end64buf); - end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g - end.endpos = end64pos; - } catch (IOException x) {} // no zip64 loc/end - } - return end; - } - } - } - zerror("zip END header not found"); - return null; //make compiler happy - } - - // Reads zip file central directory. - private void initCEN(int knownTotal) throws IOException { - if (knownTotal == -1) { - End end = findEND(); - if (end.endpos == 0) { - locpos = 0; - total = 0; - entries = new int[0]; - cen = null; - return; // only END header present - } - if (end.cenlen > end.endpos) - zerror("invalid END header (bad central directory size)"); - long cenpos = end.endpos - end.cenlen; // position of CEN table - // Get position of first local file (LOC) header, taking into - // account that there may be a stub prefixed to the zip file. - locpos = cenpos - end.cenoff; - if (locpos < 0) { - zerror("invalid END header (bad central directory offset)"); - } - // read in the CEN and END - cen = new byte[(int)(end.cenlen + ENDHDR)]; - if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { - zerror("read CEN tables failed"); - } - total = end.centot; - } else { - total = knownTotal; - } - // hash table for entries - entries = new int[total * 3]; - tablelen = ((total/2) | 1); // Odd -> fewer collisions - table = new int[tablelen]; - Arrays.fill(table, ZIP_ENDCHAIN); - int idx = 0; - int hash = 0; - int next = -1; - - // list for all meta entries - metanames = new ArrayList<>(); - - // Iterate through the entries in the central directory - int i = 0; - int hsh = 0; - int pos = 0; - int limit = cen.length - ENDHDR; - while (pos + CENHDR <= limit) { - if (i >= total) { - // This will only happen if the zip file has an incorrect - // ENDTOT field, which usually means it contains more than - // 65535 entries. - initCEN(countCENHeaders(cen, limit)); - return; - } - if (CENSIG(cen, pos) != CENSIG) - zerror("invalid CEN header (bad signature)"); - int method = CENHOW(cen, pos); - int nlen = CENNAM(cen, pos); - int elen = CENEXT(cen, pos); - int clen = CENCOM(cen, pos); - if ((CENFLG(cen, pos) & 1) != 0) - zerror("invalid CEN header (encrypted entry)"); - if (method != STORED && method != DEFLATED) - zerror("invalid CEN header (bad compression method: " + method + ")"); - if (pos + CENHDR + nlen > limit) - zerror("invalid CEN header (bad header size)"); - // Record the CEN offset and the name hash in our hash cell. - hash = hashN(cen, pos + CENHDR, nlen); - hsh = (hash & 0x7fffffff) % tablelen; - next = table[hsh]; - table[hsh] = idx; - idx = addEntry(idx, hash, next, pos); - // Adds name to metanames. - if (isMetaName(cen, pos + CENHDR, nlen)) { - metanames.add(pos); - } - // skip ext and comment - pos += (CENHDR + nlen + elen + clen); - i++; - } - total = i; - if (pos + ENDHDR != cen.length) { - zerror("invalid CEN header (bad header size)"); - } - } - - private static void zerror(String msg) throws ZipException { - throw new ZipException(msg); - } - - /* - * Returns the {@code pos} of the zip cen entry corresponding to the - * specified entry name, or -1 if not found. - */ - private int getEntryPos(byte[] name, boolean addSlash) { - if (total == 0) { - return -1; - } - int hsh = hashN(name, 0, name.length); - int idx = table[(hsh & 0x7fffffff) % tablelen]; - /* - * This while loop is an optimization where a double lookup - * for name and name+/ is being performed. The name char - * array has enough room at the end to try again with a - * slash appended if the first table lookup does not succeed. - */ - while(true) { - /* - * Search down the target hash chain for a entry whose - * 32 bit hash matches the hashed name. - */ - while (idx != ZIP_ENDCHAIN) { - if (getEntryHash(idx) == hsh) { - // The CEN name must match the specfied one - int pos = getEntryPos(idx); - if (name.length == CENNAM(cen, pos)) { - boolean matched = true; - int nameoff = pos + CENHDR; - for (int i = 0; i < name.length; i++) { - if (name[i] != cen[nameoff++]) { - matched = false; - break; - } - } - if (matched) { - return pos; - } - } - } - idx = getEntryNext(idx); - } - /* If not addSlash, or slash is already there, we are done */ - if (!addSlash || name[name.length - 1] == '/') { - return -1; - } - /* Add slash and try once more */ - name = Arrays.copyOf(name, name.length + 1); - name[name.length - 1] = '/'; - hsh = hash_append(hsh, (byte)'/'); - //idx = table[hsh % tablelen]; - idx = table[(hsh & 0x7fffffff) % tablelen]; - addSlash = false; - } - } - - private static byte[] metainf = new byte[] { - 'M', 'E', 'T', 'A', '-', 'I' , 'N', 'F', '/', - }; - - /* - * Returns true if the specified entry's name begins with the string - * "META-INF/" irrespective of case. - */ - private static boolean isMetaName(byte[] name, int off, int len) { - if (len < 9 || (name[off] != 'M' && name[off] != 'm')) { // sizeof("META-INF/") - 1 - return false; - } - off++; - for (int i = 1; i < metainf.length; i++) { - byte c = name[off++]; - // Avoid toupper; it's locale-dependent - if (c >= 'a' && c <= 'z') { - c += 'A' - 'a'; - } - if (metainf[i] != c) { - return false; - } - } - return true; - } - - /* - * Counts the number of CEN headers in a central directory extending - * from BEG to END. Might return a bogus answer if the zip file is - * corrupt, but will not crash. - */ - static int countCENHeaders(byte[] cen, int end) { - int count = 0; - int pos = 0; - while (pos + CENHDR <= end) { - count++; - pos += (CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos)); - } - return count; - } - } + private static native String getZipMessage(long jzfile); } diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java index a6632f0fa83..81882fdcec6 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java @@ -31,8 +31,6 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.concurrent.TimeUnit; -import static java.util.zip.ZipConstants.ENDHDR; - class ZipUtils { // used to adjust values between Windows and java epoch @@ -135,7 +133,7 @@ class ZipUtils { * The bytes are assumed to be in Intel (little-endian) byte order. */ public static final int get16(byte b[], int off) { - return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); + return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); } /** @@ -162,79 +160,4 @@ class ZipUtils { public static final int get32S(byte b[], int off) { return (get16(b, off) | (get16(b, off+2) << 16)); } - - // fields access methods - static final int CH(byte[] b, int n) { - return b[n] & 0xff ; - } - - static final int SH(byte[] b, int n) { - return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8); - } - - static final long LG(byte[] b, int n) { - return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL; - } - - static final long LL(byte[] b, int n) { - return (LG(b, n)) | (LG(b, n + 4) << 32); - } - - static final long GETSIG(byte[] b) { - return LG(b, 0); - } - - // local file (LOC) header fields - static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature - static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract - static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags - static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method - static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time - static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data - static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size - static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size - static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length - static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length - - // extra local (EXT) header fields - static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data - static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size - static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size - - // end of central directory header (END) fields - static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk - static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries - static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size - static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset - static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment - static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);} - - // zip64 end of central directory recoder fields - static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk - static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries - static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size - static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset - static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset - - // central directory header (CEN) fields - static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); } - static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); } - static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); } - static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); } - static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);} - static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);} - static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);} - static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);} - static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);} - static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);} - static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);} - static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);} - static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);} - static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);} - static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);} - static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);} - - // The END header is followed by a variable length comment of size < 64k. - static final long END_MAXLEN = 0xFFFF + ENDHDR; - static final int READBLOCKSZ = 128; } diff --git a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java index 9b9b1e85788..df10fda22ca 100644 --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java @@ -29,6 +29,5 @@ import java.util.zip.ZipFile; public interface JavaUtilZipFileAccess { public boolean startsWithLocHeader(ZipFile zip); - public String[] getMetaInfEntryNames(ZipFile zip); } diff --git a/jdk/src/java.base/share/classes/sun/misc/VM.java b/jdk/src/java.base/share/classes/sun/misc/VM.java index 37dc3b38fa1..0c75f10c657 100644 --- a/jdk/src/java.base/share/classes/sun/misc/VM.java +++ b/jdk/src/java.base/share/classes/sun/misc/VM.java @@ -274,6 +274,9 @@ public class VM { // used by java.lang.Integer.IntegerCache props.remove("java.lang.Integer.IntegerCache.high"); + // used by java.util.zip.ZipFile + props.remove("sun.zip.disableMemoryMapping"); + // used by sun.launcher.LauncherHelper props.remove("sun.java.launcher.diag"); } diff --git a/jdk/src/java.base/share/native/libzip/ZipFile.c b/jdk/src/java.base/share/native/libzip/ZipFile.c new file mode 100644 index 00000000000..d7a21a6cf88 --- /dev/null +++ b/jdk/src/java.base/share/native/libzip/ZipFile.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 1998, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * Native method support for java.util.zip.ZipFile + */ + +#include +#include +#include +#include +#include +#include +#include "jlong.h" +#include "jvm.h" +#include "jni.h" +#include "jni_util.h" +#include "zip_util.h" +#ifdef WIN32 +#include "io_util_md.h" +#else +#include "io_util.h" +#endif + +#include "java_util_zip_ZipFile.h" +#include "java_util_jar_JarFile.h" + +#define DEFLATED 8 +#define STORED 0 + +static jfieldID jzfileID; + +static int OPEN_READ = java_util_zip_ZipFile_OPEN_READ; +static int OPEN_DELETE = java_util_zip_ZipFile_OPEN_DELETE; + + +/* + * Declare library specific JNI_Onload entry if static build + */ +DEF_STATIC_JNI_OnLoad + +JNIEXPORT void JNICALL +Java_java_util_zip_ZipFile_initIDs(JNIEnv *env, jclass cls) +{ + jzfileID = (*env)->GetFieldID(env, cls, "jzfile", "J"); + assert(jzfileID != 0); +} + +static void +ThrowZipException(JNIEnv *env, const char *msg) +{ + jstring s = NULL; + jobject x; + + if (msg != NULL) { + s = JNU_NewStringPlatform(env, msg); + } + if (s != NULL) { + x = JNU_NewObjectByName(env, + "java/util/zip/ZipException", + "(Ljava/lang/String;)V", s); + if (x != NULL) { + (*env)->Throw(env, x); + } + } +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_open(JNIEnv *env, jclass cls, jstring name, + jint mode, jlong lastModified, + jboolean usemmap) +{ + const char *path = JNU_GetStringPlatformChars(env, name, 0); + char *msg = 0; + jlong result = 0; + int flag = 0; + jzfile *zip = 0; + + if (mode & OPEN_READ) flag |= O_RDONLY; + + if (path != 0) { + zip = ZIP_Get_From_Cache(path, &msg, lastModified); + if (zip == 0 && msg == 0) { + ZFILE zfd = 0; +#ifdef WIN32 + if (mode & OPEN_DELETE) flag |= O_TEMPORARY; + zfd = winFileHandleOpen(env, name, flag); + if (zfd == -1) { + /* Exception already pending. */ + goto finally; + } +#else + zfd = open(path, flag, 0); + if (zfd < 0) { + throwFileNotFoundException(env, name); + goto finally; + } + if (mode & OPEN_DELETE) { + unlink(path); + } +#endif + zip = ZIP_Put_In_Cache0(path, zfd, &msg, lastModified, usemmap); + } + + if (zip != 0) { + result = ptr_to_jlong(zip); + } else if (msg != 0) { + ThrowZipException(env, msg); + free(msg); + } else if (errno == ENOMEM) { + JNU_ThrowOutOfMemoryError(env, 0); + } else { + ThrowZipException(env, "error in opening zip file"); + } +finally: + JNU_ReleaseStringPlatformChars(env, name, path); + } + return result; +} + +JNIEXPORT jint JNICALL +Java_java_util_zip_ZipFile_getTotal(JNIEnv *env, jclass cls, jlong zfile) +{ + jzfile *zip = jlong_to_ptr(zfile); + + return zip->total; +} + +JNIEXPORT jboolean JNICALL +Java_java_util_zip_ZipFile_startsWithLOC(JNIEnv *env, jclass cls, jlong zfile) +{ + jzfile *zip = jlong_to_ptr(zfile); + + return zip->locsig; +} + +JNIEXPORT void JNICALL +Java_java_util_zip_ZipFile_close(JNIEnv *env, jclass cls, jlong zfile) +{ + ZIP_Close(jlong_to_ptr(zfile)); +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile, + jbyteArray name, jboolean addSlash) +{ +#define MAXNAME 1024 + jzfile *zip = jlong_to_ptr(zfile); + jsize ulen = (*env)->GetArrayLength(env, name); + char buf[MAXNAME+2], *path; + jzentry *ze; + + if (ulen > MAXNAME) { + path = malloc(ulen + 2); + if (path == 0) { + JNU_ThrowOutOfMemoryError(env, 0); + return 0; + } + } else { + path = buf; + } + (*env)->GetByteArrayRegion(env, name, 0, ulen, (jbyte *)path); + path[ulen] = '\0'; + ze = ZIP_GetEntry2(zip, path, (jint)ulen, addSlash); + if (path != buf) { + free(path); + } + return ptr_to_jlong(ze); +} + +JNIEXPORT void JNICALL +Java_java_util_zip_ZipFile_freeEntry(JNIEnv *env, jclass cls, jlong zfile, + jlong zentry) +{ + jzfile *zip = jlong_to_ptr(zfile); + jzentry *ze = jlong_to_ptr(zentry); + ZIP_FreeEntry(zip, ze); +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getNextEntry(JNIEnv *env, jclass cls, jlong zfile, + jint n) +{ + jzentry *ze = ZIP_GetNextEntry(jlong_to_ptr(zfile), n); + return ptr_to_jlong(ze); +} + +JNIEXPORT jint JNICALL +Java_java_util_zip_ZipFile_getEntryMethod(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return ze->csize != 0 ? DEFLATED : STORED; +} + +JNIEXPORT jint JNICALL +Java_java_util_zip_ZipFile_getEntryFlag(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return ze->flag; +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntryCSize(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return ze->csize != 0 ? ze->csize : ze->size; +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntrySize(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return ze->size; +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntryTime(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return (jlong)ze->time & 0xffffffffUL; +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntryCrc(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return (jlong)ze->crc & 0xffffffffUL; +} + +JNIEXPORT jbyteArray JNICALL +Java_java_util_zip_ZipFile_getCommentBytes(JNIEnv *env, + jclass cls, + jlong zfile) +{ + jzfile *zip = jlong_to_ptr(zfile); + jbyteArray jba = NULL; + + if (zip->comment != NULL) { + if ((jba = (*env)->NewByteArray(env, zip->clen)) == NULL) + return NULL; + (*env)->SetByteArrayRegion(env, jba, 0, zip->clen, (jbyte*)zip->comment); + } + return jba; +} + +JNIEXPORT jbyteArray JNICALL +Java_java_util_zip_ZipFile_getEntryBytes(JNIEnv *env, + jclass cls, + jlong zentry, jint type) +{ + jzentry *ze = jlong_to_ptr(zentry); + int len = 0; + jbyteArray jba = NULL; + switch (type) { + case java_util_zip_ZipFile_JZENTRY_NAME: + if (ze->name != 0) { + len = (int)ze->nlen; + // Unlike for extra and comment, we never return null for + // an (extremely rarely seen) empty name + if ((jba = (*env)->NewByteArray(env, len)) == NULL) + break; + (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte *)ze->name); + } + break; + case java_util_zip_ZipFile_JZENTRY_EXTRA: + if (ze->extra != 0) { + unsigned char *bp = (unsigned char *)&ze->extra[0]; + len = (bp[0] | (bp[1] << 8)); + if (len <= 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) + break; + (*env)->SetByteArrayRegion(env, jba, 0, len, &ze->extra[2]); + } + break; + case java_util_zip_ZipFile_JZENTRY_COMMENT: + if (ze->comment != 0) { + len = (int)strlen(ze->comment); + if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) + break; + (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte*)ze->comment); + } + break; + } + return jba; +} + +JNIEXPORT jint JNICALL +Java_java_util_zip_ZipFile_read(JNIEnv *env, jclass cls, jlong zfile, + jlong zentry, jlong pos, jbyteArray bytes, + jint off, jint len) +{ + jzfile *zip = jlong_to_ptr(zfile); + char *msg; + +#define BUFSIZE 8192 + /* copy via tmp stack buffer: */ + jbyte buf[BUFSIZE]; + + if (len > BUFSIZE) { + len = BUFSIZE; + } + + ZIP_Lock(zip); + len = ZIP_Read(zip, jlong_to_ptr(zentry), pos, buf, len); + msg = zip->msg; + ZIP_Unlock(zip); + if (len != -1) { + (*env)->SetByteArrayRegion(env, bytes, off, len, buf); + } + + if (len == -1) { + if (msg != 0) { + ThrowZipException(env, msg); + } else { + char errmsg[128]; + sprintf(errmsg, "errno: %d, error: %s\n", + errno, "Error reading ZIP file"); + JNU_ThrowIOExceptionWithLastError(env, errmsg); + } + } + + return len; +} + +/* + * Returns an array of strings representing the names of all entries + * that begin with "META-INF/" (case ignored). This native method is + * used in JarFile as an optimization when looking up manifest and + * signature file entries. Returns null if no entries were found. + */ +JNIEXPORT jobjectArray JNICALL +Java_java_util_jar_JarFile_getMetaInfEntryNames(JNIEnv *env, jobject obj) +{ + jlong zfile = (*env)->GetLongField(env, obj, jzfileID); + jzfile *zip; + int i, count; + jobjectArray result = 0; + + if (zfile == 0) { + JNU_ThrowByName(env, + "java/lang/IllegalStateException", "zip file closed"); + return NULL; + } + zip = jlong_to_ptr(zfile); + + /* count the number of valid ZIP metanames */ + count = 0; + if (zip->metanames != 0) { + for (i = 0; i < zip->metacount; i++) { + if (zip->metanames[i] != 0) { + count++; + } + } + } + + /* If some names were found then build array of java strings */ + if (count > 0) { + jclass cls = JNU_ClassString(env); + CHECK_NULL_RETURN(cls, NULL); + result = (*env)->NewObjectArray(env, count, cls, 0); + CHECK_NULL_RETURN(result, NULL); + if (result != 0) { + for (i = 0; i < count; i++) { + jstring str = (*env)->NewStringUTF(env, zip->metanames[i]); + if (str == 0) { + break; + } + (*env)->SetObjectArrayElement(env, result, i, str); + (*env)->DeleteLocalRef(env, str); + } + } + } + return result; +} + +JNIEXPORT jstring JNICALL +Java_java_util_zip_ZipFile_getZipMessage(JNIEnv *env, jclass cls, jlong zfile) +{ + jzfile *zip = jlong_to_ptr(zfile); + char *msg = zip->msg; + if (msg == NULL) { + return NULL; + } + return JNU_NewStringPlatform(env, msg); +} diff --git a/jdk/test/java/util/zip/ZipFile/ReadZip.java b/jdk/test/java/util/zip/ZipFile/ReadZip.java index fe923e81eee..1052642eda7 100644 --- a/jdk/test/java/util/zip/ZipFile/ReadZip.java +++ b/jdk/test/java/util/zip/ZipFile/ReadZip.java @@ -30,7 +30,6 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; -import java.nio.file.NoSuchFileException; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.zip.*; @@ -111,6 +110,6 @@ public class ReadZip { "input" + String.valueOf(new java.util.Random().nextInt()) + ".zip"))); - } catch (NoSuchFileException nsfe) {} + } catch (FileNotFoundException fnfe) {} } } diff --git a/jdk/test/java/util/zip/ZipFile/TestZipFile.java b/jdk/test/java/util/zip/ZipFile/TestZipFile.java deleted file mode 100644 index 986877731db..00000000000 --- a/jdk/test/java/util/zip/ZipFile/TestZipFile.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * 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. - */ - -/* - * @test - * @bug 8142508 - * @summary Tests various ZipFile apis - * @run main/manual TestZipFile - */ - -import java.io.*; -import java.lang.reflect.Method; -import java.nio.*; -import java.nio.file.*; -import java.nio.file.attribute.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.zip.*; - -public class TestZipFile { - - private static Random r = new Random(); - private static int N = 50; - private static int NN = 10; - private static int ENUM = 10000; - private static int ESZ = 10000; - private static ExecutorService executor = Executors.newFixedThreadPool(20); - private static Set paths = new HashSet<>(); - - static void realMain (String[] args) throws Throwable { - - try { - for (int i = 0; i < N; i++) { - test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); - test(r.nextInt(ENUM), r.nextInt(ESZ), true, true); - } - - for (int i = 0; i < NN; i++) { - test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), false, true); - test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), true, true); - testCachedDelete(); - testCachedOverwrite(); - //test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); - } - - test(70000, 1000, false, true); // > 65536 entry number; - testDelete(); // OPEN_DELETE - - executor.shutdown(); - executor.awaitTermination(10, TimeUnit.MINUTES); - } finally { - for (Path path : paths) { - Files.deleteIfExists(path); - } - } - } - - static void test(int numEntry, int szMax, boolean addPrefix, boolean cleanOld) { - String name = "zftest" + r.nextInt() + ".zip"; - Zip zip = new Zip(name, numEntry, szMax, addPrefix, cleanOld); - for (int i = 0; i < NN; i++) { - executor.submit(() -> doTest(zip)); - } - } - - // test scenario: - // (1) open the ZipFile(zip) with OPEN_READ | OPEN_DELETE - // (2) test the ZipFile works correctly - // (3) check the zip is deleted after ZipFile gets closed - static void testDelete() throws Throwable { - String name = "zftest" + r.nextInt() + ".zip"; - Zip zip = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); - try (ZipFile zf = new ZipFile(new File(zip.name), - ZipFile.OPEN_READ | ZipFile.OPEN_DELETE )) - { - doTest0(zip, zf); - } - Path p = Paths.get(name); - if (Files.exists(p)) { - fail("Failed to delete " + name + " with OPEN_DELETE"); - } - } - - // test scenario: - // (1) keep a ZipFile(zip1) alive (in ZipFile's cache), dont close it - // (2) delete zip1 and create zip2 with the same name the zip1 with zip2 - // (3) zip1 tests should fail, but no crash - // (4) zip2 tasks should all get zip2, then pass normal testing. - static void testCachedDelete() throws Throwable { - String name = "zftest" + r.nextInt() + ".zip"; - Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); - - try (ZipFile zf = new ZipFile(zip1.name)) { - for (int i = 0; i < NN; i++) { - executor.submit(() -> verifyNoCrash(zip1)); - } - // delete the "zip1" and create a new one to test - Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); - /* - System.out.println("========================================"); - System.out.printf(" zip1=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", - zip1.name, zip1.lastModified, zip1.entries.size(), - zip1.attrs.fileKey(), zip1.attrs.size(), zip1.attrs.lastModifiedTime().toMillis()); - System.out.printf(" zip2=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", - zip2.name, zip2.lastModified, zip2.entries.size(), - zip2.attrs.fileKey(), zip2.attrs.size(), zip2.attrs.lastModifiedTime().toMillis()); - */ - for (int i = 0; i < NN; i++) { - executor.submit(() -> doTest(zip2)); - } - } - } - - // overwrite the "zip1" and create a new one to test. So the two zip files - // have the same fileKey, but probably different lastModified() - static void testCachedOverwrite() throws Throwable { - String name = "zftest" + r.nextInt() + ".zip"; - Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); - try (ZipFile zf = new ZipFile(zip1.name)) { - for (int i = 0; i < NN; i++) { - executor.submit(() -> verifyNoCrash(zip1)); - } - // overwrite the "zip1" with new contents - Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, false); - for (int i = 0; i < NN; i++) { - executor.submit(() -> doTest(zip2)); - } - } - } - - // just check the entries and contents. since the file has been either overwritten - // or deleted/rewritten, we only care if it crahes or not. - static void verifyNoCrash(Zip zip) throws RuntimeException { - try (ZipFile zf = new ZipFile(zip.name)) { - List zlist = new ArrayList(zip.entries.keySet()); - String[] elist = zf.stream().map( e -> e.getName()).toArray(String[]::new); - if (!Arrays.equals(elist, - zlist.stream().map( e -> e.getName()).toArray(String[]::new))) - { - //System.out.printf("++++++ LIST NG [%s] entries.len=%d, expected=%d+++++++%n", - // zf.getName(), elist.length, zlist.size()); - return; - } - for (ZipEntry ze : zlist) { - byte[] zdata = zip.entries.get(ze); - ZipEntry e = zf.getEntry(ze.getName()); - if (e != null) { - checkEqual(e, ze); - if (!e.isDirectory()) { - // check with readAllBytes - try (InputStream is = zf.getInputStream(e)) { - if (!Arrays.equals(zdata, is.readAllBytes())) { - //System.out.printf("++++++ BYTES NG [%s]/[%s] ++++++++%n", - // zf.getName(), ze.getName()); - } - } - } - } - } - } catch (Throwable t) { - // t.printStackTrace(); - // fail(t.toString()); - } - } - - static void checkEqual(ZipEntry x, ZipEntry y) { - if (x.getName().equals(y.getName()) && - x.isDirectory() == y.isDirectory() && - x.getMethod() == y.getMethod() && - (x.getTime() / 2000) == y.getTime() / 2000 && - x.getSize() == y.getSize() && - x.getCompressedSize() == y.getCompressedSize() && - x.getCrc() == y.getCrc() && - x.getComment().equals(y.getComment()) - ) { - pass(); - } else { - fail(x + " not equal to " + y); - System.out.printf(" %s %s%n", x.getName(), y.getName()); - System.out.printf(" %d %d%n", x.getMethod(), y.getMethod()); - System.out.printf(" %d %d%n", x.getTime(), y.getTime()); - System.out.printf(" %d %d%n", x.getSize(), y.getSize()); - System.out.printf(" %d %d%n", x.getCompressedSize(), y.getCompressedSize()); - System.out.printf(" %d %d%n", x.getCrc(), y.getCrc()); - System.out.println("-----------------"); - } - } - - static void doTest(Zip zip) throws RuntimeException { - //Thread me = Thread.currentThread(); - try (ZipFile zf = new ZipFile(zip.name)) { - doTest0(zip, zf); - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - static void doTest0(Zip zip, ZipFile zf) throws Throwable { - List list = new ArrayList(zip.entries.keySet()); - // (1) check entry list, in expected order - if (!check(Arrays.equals( - list.stream().map( e -> e.getName()).toArray(String[]::new), - zf.stream().map( e -> e.getName()).toArray(String[]::new)))) { - return; - } - // (2) shuffle, and check each entry and its bytes - Collections.shuffle(list); - for (ZipEntry ze : list) { - byte[] data = zip.entries.get(ze); - ZipEntry e = zf.getEntry(ze.getName()); - checkEqual(e, ze); - if (!e.isDirectory()) { - // check with readAllBytes - try (InputStream is = zf.getInputStream(e)) { - check(Arrays.equals(data, is.readAllBytes())); - } - // check with smaller sized buf - try (InputStream is = zf.getInputStream(e)) { - byte[] buf = new byte[(int)e.getSize()]; - int sz = r.nextInt((int)e.getSize()/4 + 1) + 1; - int off = 0; - int n; - while ((n = is.read(buf, off, buf.length - off)) > 0) { - off += n; - } - check(is.read() == -1); - check(Arrays.equals(data, buf)); - } - } - } - // (3) check getMetaInfEntryNames - String[] metas = list.stream() - .map( e -> e.getName()) - .filter( s -> s.startsWith("META-INF/")) - .sorted() - .toArray(String[]::new); - if (metas.length > 0) { - // meta-inf entries - Method getMetas = ZipFile.class.getDeclaredMethod("getMetaInfEntryNames"); - getMetas.setAccessible(true); - String[] names = (String[])getMetas.invoke(zf); - if (names == null) { - fail("Failed to get metanames from " + zf); - } else { - Arrays.sort(names); - check(Arrays.equals(names, metas)); - } - } - } - - private static class Zip { - String name; - Map entries; - BasicFileAttributes attrs; - long lastModified; - - Zip(String name, int num, int szMax, boolean prefix, boolean clean) { - this.name = name; - entries = new LinkedHashMap<>(num); - try { - Path p = Paths.get(name); - if (clean) { - Files.deleteIfExists(p); - } - paths.add(p); - } catch (Exception x) { - throw (RuntimeException)x; - } - - try (FileOutputStream fos = new FileOutputStream(name); - BufferedOutputStream bos = new BufferedOutputStream(fos); - ZipOutputStream zos = new ZipOutputStream(bos)) - { - if (prefix) { - byte[] bytes = new byte[r.nextInt(1000)]; - r.nextBytes(bytes); - bos.write(bytes); - } - CRC32 crc = new CRC32(); - for (int i = 0; i < num; i++) { - String ename = "entry-" + i + "-name-" + r.nextLong(); - ZipEntry ze = new ZipEntry(ename); - int method = r.nextBoolean() ? ZipEntry.STORED : ZipEntry.DEFLATED; - writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); - } - // add some manifest entries - for (int i = 0; i < r.nextInt(20); i++) { - String meta = "META-INF/" + "entry-" + i + "-metainf-" + r.nextLong(); - ZipEntry ze = new ZipEntry(meta); - writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); - } - } catch (Exception x) { - throw (RuntimeException)x; - } - try { - this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class); - this.lastModified = new File(name).lastModified(); - } catch (Exception x) { - throw (RuntimeException)x; - } - } - - private void writeEntry(ZipOutputStream zos, CRC32 crc, - ZipEntry ze, int method, int szMax) - throws IOException - { - ze.setMethod(method); - byte[] data = new byte[r.nextInt(szMax + 1)]; - r.nextBytes(data); - if (method == ZipEntry.STORED) { // must set size/csize/crc - ze.setSize(data.length); - ze.setCompressedSize(data.length); - crc.reset(); - crc.update(data); - ze.setCrc(crc.getValue()); - } - ze.setTime(System.currentTimeMillis()); - ze.setComment(ze.getName()); - zos.putNextEntry(ze); - zos.write(data); - zos.closeEntry(); - entries.put(ze, data); - } - } - - //--------------------- Infrastructure --------------------------- - static volatile int passed = 0, failed = 0; - static void pass() {passed++;} - static void pass(String msg) {System.out.println(msg); passed++;} - static void fail() {failed++; Thread.dumpStack();} - static void fail(String msg) {System.out.println(msg); fail();} - static void unexpected(Throwable t) {failed++; t.printStackTrace();} - static void unexpected(Throwable t, String msg) { - System.out.println(msg); failed++; t.printStackTrace();} - static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} - - public static void main(String[] args) throws Throwable { - try {realMain(args);} catch (Throwable t) {unexpected(t);} - System.out.println("\nPassed = " + passed + " failed = " + failed); - if (failed > 0) throw new AssertionError("Some tests failed");} -} From d42e70fc3c5552af8dcaa1375322f9464019efad Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Tue, 8 Dec 2015 13:48:22 -0800 Subject: [PATCH 015/199] 8139232: JEP-269 initial API and skeleton implementations Reviewed-by: psandoz, rriggs --- .../classes/java/util/KeyValueHolder.java | 126 +++++ .../share/classes/java/util/List.java | 332 +++++++++++- .../share/classes/java/util/Map.java | 496 +++++++++++++++++- .../share/classes/java/util/Set.java | 362 ++++++++++++- jdk/test/java/util/Collection/MOAT.java | 125 ++++- .../java/util/Collection/SetFactories.java | 275 ++++++++++ jdk/test/java/util/List/ListFactories.java | 234 +++++++++ jdk/test/java/util/Map/MapFactories.java | 380 ++++++++++++++ 8 files changed, 2315 insertions(+), 15 deletions(-) create mode 100644 jdk/src/java.base/share/classes/java/util/KeyValueHolder.java create mode 100644 jdk/test/java/util/Collection/SetFactories.java create mode 100644 jdk/test/java/util/List/ListFactories.java create mode 100644 jdk/test/java/util/Map/MapFactories.java diff --git a/jdk/src/java.base/share/classes/java/util/KeyValueHolder.java b/jdk/src/java.base/share/classes/java/util/KeyValueHolder.java new file mode 100644 index 00000000000..96ca0753ac6 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/util/KeyValueHolder.java @@ -0,0 +1,126 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package java.util; + +/** + * An immutable container for a key and a value, suitable for use + * in creating and populating {@code Map} instances. + * + *

This is a value-based + * class; use of identity-sensitive operations (including reference equality + * ({@code ==}), identity hash code, or synchronization) on instances of + * {@code KeyValueHolder} may have unpredictable results and should be avoided. + * + * @apiNote + * This class is not public. Instances can be created using the + * {@link Map#entry Map.entry(k, v)} factory method, which is public. + * + *

This class differs from AbstractMap.SimpleImmutableEntry in the following ways: + * it is not serializable, it is final, and its key and value must be non-null. + * + * @param the key type + * @param the value type + * + * @see Map#ofEntries Map.ofEntries() + * @since 9 + */ +final class KeyValueHolder implements Map.Entry { + final K key; + final V value; + + KeyValueHolder(K k, V v) { + key = Objects.requireNonNull(k); + value = Objects.requireNonNull(v); + } + + /** + * Gets the key from this holder. + * + * @return the key + */ + @Override + public K getKey() { + return key; + } + + /** + * Gets the value from this holder. + * + * @return the value + */ + @Override + public V getValue() { + return value; + } + + /** + * Throws {@link UnsupportedOperationException}. + * + * @param value ignored + * @return never returns normally + */ + @Override + public V setValue(V value) { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Compares the specified object with this entry for equality. + * Returns {@code true} if the given object is also a map entry and + * the two entries' keys and values are equal. Note that key and + * value are non-null, so equals() can be called safely on them. + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry e = (Map.Entry)o; + return key.equals(e.getKey()) && value.equals(e.getValue()); + } + + /** + * Returns the hash code value for this map entry. The hash code + * is {@code key.hashCode() ^ value.hashCode()}. Note that key and + * value are non-null, so hashCode() can be called safely on them. + */ + @Override + public int hashCode() { + return key.hashCode() ^ value.hashCode(); + } + + /** + * Returns a String representation of this map entry. This + * implementation returns the string representation of this + * entry's key followed by the equals character ("{@code =}") + * followed by the string representation of this entry's value. + * + * @return a String representation of this map entry + */ + @Override + public String toString() { + return key + "=" + value; + } +} diff --git a/jdk/src/java.base/share/classes/java/util/List.java b/jdk/src/java.base/share/classes/java/util/List.java index 6e3751fa046..48464801316 100644 --- a/jdk/src/java.base/share/classes/java/util/List.java +++ b/jdk/src/java.base/share/classes/java/util/List.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -87,6 +87,28 @@ import java.util.function.UnaryOperator; * Such exceptions are marked as "optional" in the specification for this * interface. * + *

Immutable List Static Factory Methods

+ *

The {@link List#of(Object...) List.of()} static factory methods + * provide a convenient way to create immutable lists. The {@code List} + * instances created by these methods have the following characteristics: + * + *

    + *
  • They are structurally immutable. Elements cannot be added, removed, + * or replaced. Attempts to do so result in {@code UnsupportedOperationException}. + * However, if the contained elements are themselves mutable, + * this may cause the List's contents to appear to change. + *
  • They disallow {@code null} elements. Attempts to create them with + * {@code null} elements result in {@code NullPointerException}. + *
  • They are serializable if all elements are serializable. + *
  • The order of elements in the list is the same as the order of the + * provided arguments, or of the elements in the provided array. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned instances. + * Factories are free to create new instances or reuse existing ones. Therefore, + * identity-sensitive operations on these instances (reference equality ({@code ==}), + * identity hash code, and synchronization) are unreliable and should be avoided. + *
+ * *

This interface is a member of the * * Java Collections Framework. @@ -731,4 +753,312 @@ public interface List extends Collection { default Spliterator spliterator() { return Spliterators.spliterator(this, Spliterator.ORDERED); } + + /** + * Returns an immutable list containing zero elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @return an empty {@code List} + * + * @since 9 + */ + static List of() { + return Collections.emptyList(); + } + + /** + * Returns an immutable list containing one element. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the single element + * @return a {@code List} containing the specified element + * @throws NullPointerException if the element is {@code null} + * + * @since 9 + */ + static List of(E e1) { + return Collections.singletonList(Objects.requireNonNull(e1)); + } + + /** + * Returns an immutable list containing two elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2))); + } + + /** + * Returns an immutable list containing three elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3))); + } + + /** + * Returns an immutable list containing four elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4))); + } + + /** + * Returns an immutable list containing five elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5))); + } + + /** + * Returns an immutable list containing six elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6))); + } + + /** + * Returns an immutable list containing seven elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7))); + } + + /** + * Returns an immutable list containing eight elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8))); + } + + /** + * Returns an immutable list containing nine elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @param e9 the ninth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8), + Objects.requireNonNull(e9))); + } + + /** + * Returns an immutable list containing ten elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @param e9 the ninth element + * @param e10 the tenth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8), + Objects.requireNonNull(e9), + Objects.requireNonNull(e10))); + } + + /** + * Returns an immutable list containing an arbitrary number of elements. + * See Immutable List Static Factory Methods for details. + * + * @apiNote + * This method also accepts a single array as an argument. The element type of + * the resulting list will be the component type of the array, and the size of + * the list will be equal to the length of the array. To create a list with + * a single element that is an array, do the following: + * + *

{@code
+     *     String[] array = ... ;
+     *     List list = List.of(array);
+     * }
+ * + * This will cause the {@link List#of(Object) List.of(E)} method + * to be invoked instead. + * + * @param the {@code List}'s element type + * @param elements the elements to be contained in the list + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} or if the array is {@code null} + * + * @since 9 + */ + @SafeVarargs + @SuppressWarnings("varargs") + static List of(E... elements) { + elements = elements.clone(); // throws NPE if es is null + for (E e : elements) { + Objects.requireNonNull(e); + } + return Collections.unmodifiableList(Arrays.asList(elements)); + } } diff --git a/jdk/src/java.base/share/classes/java/util/Map.java b/jdk/src/java.base/share/classes/java/util/Map.java index 9d79c6ce7a9..f958607dfc5 100644 --- a/jdk/src/java.base/share/classes/java/util/Map.java +++ b/jdk/src/java.base/share/classes/java/util/Map.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -110,6 +110,31 @@ import java.io.Serializable; * Implementations may optionally handle the self-referential scenario, however * most current implementations do not do so. * + *

Immutable Map Static Factory Methods

+ *

The {@link Map#of() Map.of()} and + * {@link Map#ofEntries(Map.Entry...) Map.ofEntries()} + * static factory methods provide a convenient way to create immutable maps. + * The {@code Map} + * instances created by these methods have the following characteristics: + * + *

    + *
  • They are structurally immutable. Keys and values cannot be added, + * removed, or updated. Attempts to do so result in {@code UnsupportedOperationException}. + * However, if the contained keys or values are themselves mutable, this may cause the + * Map to behave inconsistently or its contents to appear to change. + *
  • They disallow {@code null} keys and values. Attempts to create them with + * {@code null} keys or values result in {@code NullPointerException}. + *
  • They are serializable if all keys and values are serializable. + *
  • They reject duplicate keys at creation time. Duplicate keys + * passed to a static factory method result in {@code IllegalArgumentException}. + *
  • The iteration order of mappings is unspecified and is subject to change. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned instances. + * Factories are free to create new instances or reuse existing ones. Therefore, + * identity-sensitive operations on these instances (reference equality ({@code ==}), + * identity hash code, and synchronization) are unreliable and should be avoided. + *
+ * *

This interface is a member of the * * Java Collections Framework. @@ -126,7 +151,7 @@ import java.io.Serializable; * @see Set * @since 1.2 */ -public interface Map { +public interface Map { // Query Operations /** @@ -373,7 +398,7 @@ public interface Map { * @see Map#entrySet() * @since 1.2 */ - interface Entry { + interface Entry { /** * Returns the key corresponding to this entry. * @@ -468,7 +493,7 @@ public interface Map { * @see Comparable * @since 1.8 */ - public static , V> Comparator> comparingByKey() { + public static , V> Comparator> comparingByKey() { return (Comparator> & Serializable) (c1, c2) -> c1.getKey().compareTo(c2.getKey()); } @@ -485,7 +510,7 @@ public interface Map { * @see Comparable * @since 1.8 */ - public static > Comparator> comparingByValue() { + public static > Comparator> comparingByValue() { return (Comparator> & Serializable) (c1, c2) -> c1.getValue().compareTo(c2.getValue()); } @@ -1233,4 +1258,465 @@ public interface Map { } return newValue; } + + /** + * Returns an immutable map containing zero mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @return an empty {@code Map} + * + * @since 9 + */ + static Map of() { + return Collections.emptyMap(); + } + + /** + * Returns an immutable map containing a single mapping. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the mapping's key + * @param v1 the mapping's value + * @return a {@code Map} containing the specified mapping + * @throws NullPointerException if the key or the value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1) { + return Collections.singletonMap(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + } + + /** + * Returns an immutable map containing two mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if the keys are duplicates + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2) { + Map map = new HashMap<>(3); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + if (map.size() != 2) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing three mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3) { + Map map = new HashMap<>(5); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + if (map.size() != 3) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing four mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + Map map = new HashMap<>(6); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + if (map.size() != 4) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing five mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + Map map = new HashMap<>(7); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + if (map.size() != 5) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing six mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6) { + Map map = new HashMap<>(9); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + if (map.size() != 6) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing seven mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @param k7 the seventh mapping's key + * @param v7 the seventh mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6, K k7, V v7) { + Map map = new HashMap<>(10); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + map.put(Objects.requireNonNull(k7), Objects.requireNonNull(v7)); + if (map.size() != 7) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing eight mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @param k7 the seventh mapping's key + * @param v7 the seventh mapping's value + * @param k8 the eighth mapping's key + * @param v8 the eighth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6, K k7, V v7, K k8, V v8) { + Map map = new HashMap<>(11); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + map.put(Objects.requireNonNull(k7), Objects.requireNonNull(v7)); + map.put(Objects.requireNonNull(k8), Objects.requireNonNull(v8)); + if (map.size() != 8) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing nine mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @param k7 the seventh mapping's key + * @param v7 the seventh mapping's value + * @param k8 the eighth mapping's key + * @param v8 the eighth mapping's value + * @param k9 the ninth mapping's key + * @param v9 the ninth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) { + Map map = new HashMap<>(13); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + map.put(Objects.requireNonNull(k7), Objects.requireNonNull(v7)); + map.put(Objects.requireNonNull(k8), Objects.requireNonNull(v8)); + map.put(Objects.requireNonNull(k9), Objects.requireNonNull(v9)); + if (map.size() != 9) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing ten mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @param k7 the seventh mapping's key + * @param v7 the seventh mapping's value + * @param k8 the eighth mapping's key + * @param v8 the eighth mapping's value + * @param k9 the ninth mapping's key + * @param v9 the ninth mapping's value + * @param k10 the tenth mapping's key + * @param v10 the tenth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) { + Map map = new HashMap<>(14); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + map.put(Objects.requireNonNull(k7), Objects.requireNonNull(v7)); + map.put(Objects.requireNonNull(k8), Objects.requireNonNull(v8)); + map.put(Objects.requireNonNull(k9), Objects.requireNonNull(v9)); + map.put(Objects.requireNonNull(k10), Objects.requireNonNull(v10)); + if (map.size() != 10) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing keys and values extracted from the given entries. + * The entries themselves are not stored in the map. + * See Immutable Map Static Factory Methods for details. + * + * @apiNote + * It is convenient to create the map entries using the {@link Map#entry Map.entry()} method. + * For example, + * + *

{@code
+     *     import static java.util.Map.entry;
+     *
+     *     Map map = Map.ofEntries(
+     *         entry(1, "a"),
+     *         entry(2, "b"),
+     *         entry(3, "c"),
+     *         ...
+     *         entry(26, "z"));
+     * }
+ * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param entries {@code Map.Entry}s containing the keys and values from which the map is populated + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any entry, key, or value is {@code null}, or if + * the {@code entries} array is {@code null} + * + * @see Map#entry Map.entry() + * @since 9 + */ + @SafeVarargs + @SuppressWarnings("varargs") + static Map ofEntries(Entry... entries) { + Map map = new HashMap<>(entries.length * 4 / 3 + 1); // throws NPE if entries is null + for (Entry e : entries) { + // next line throws NPE if e is null + map.put(Objects.requireNonNull(e.getKey()), Objects.requireNonNull(e.getValue())); + } + if (map.size() != entries.length) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable {@link Entry} containing the given key and value. + * These entries are suitable for populating {@code Map} instances using the + * {@link Map#ofEntries Map.ofEntries()} method. + * The {@code Entry} instances created by this method have the following characteristics: + * + *
    + *
  • They disallow {@code null} keys and values. Attempts to create them using a {@code null} + * key or value result in {@code NullPointerException}. + *
  • They are immutable. Calls to {@link Entry#setValue Entry.setValue()} + * on a returned {@code Entry} result in {@code UnsupportedOperationException}. + *
  • They are not serializable. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned instances. + * This method is free to create new instances or reuse existing ones. Therefore, + * identity-sensitive operations on these instances (reference equality ({@code ==}), + * identity hash code, and synchronization) are unreliable and should be avoided. + *
+ * + * @apiNote + * For a serializable {@code Entry}, see {@link AbstractMap.SimpleEntry} or + * {@link AbstractMap.SimpleImmutableEntry}. + * + * @param the key's type + * @param the value's type + * @param k the key + * @param v the value + * @return an {@code Entry} containing the specified key and value + * @throws NullPointerException if the key or value is {@code null} + * + * @see Map#ofEntries Map.ofEntries() + * @since 9 + */ + static Entry entry(K k, V v) { + // KeyValueHolder checks for nulls + return new KeyValueHolder<>(k, v); + } } diff --git a/jdk/src/java.base/share/classes/java/util/Set.java b/jdk/src/java.base/share/classes/java/util/Set.java index 93d008c20f1..b0db7fc55bd 100644 --- a/jdk/src/java.base/share/classes/java/util/Set.java +++ b/jdk/src/java.base/share/classes/java/util/Set.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -63,6 +63,29 @@ package java.util; * Such exceptions are marked as "optional" in the specification for this * interface. * + *

Immutable Set Static Factory Methods

+ *

The {@link Set#of(Object...) Set.of()} static factory methods + * provide a convenient way to create immutable sets. The {@code Set} + * instances created by these methods have the following characteristics: + * + *

    + *
  • They are structurally immutable. Elements cannot be added or + * removed. Attempts to do so result in {@code UnsupportedOperationException}. + * However, if the contained elements are themselves mutable, this may cause the + * Set to behave inconsistently or its contents to appear to change. + *
  • They disallow {@code null} elements. Attempts to create them with + * {@code null} elements result in {@code NullPointerException}. + *
  • They are serializable if all elements are serializable. + *
  • They reject duplicate elements at creation time. Duplicate elements + * passed to a static factory method result in {@code IllegalArgumentException}. + *
  • The iteration order of set elements is unspecified and is subject to change. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned instances. + * Factories are free to create new instances or reuse existing ones. Therefore, + * identity-sensitive operations on these instances (reference equality ({@code ==}), + * identity hash code, and synchronization) are unreliable and should be avoided. + *
+ * *

This interface is a member of the * * Java Collections Framework. @@ -410,4 +433,341 @@ public interface Set extends Collection { default Spliterator spliterator() { return Spliterators.spliterator(this, Spliterator.DISTINCT); } + + /** + * Returns an immutable set containing zero elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @return an empty {@code Set} + * + * @since 9 + */ + static Set of() { + return Collections.emptySet(); + } + + /** + * Returns an immutable set containing one element. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the single element + * @return a {@code Set} containing the specified element + * @throws NullPointerException if the element is {@code null} + * + * @since 9 + */ + static Set of(E e1) { + return Collections.singleton(Objects.requireNonNull(e1)); + } + + /** + * Returns an immutable set containing two elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if the elements are duplicates + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2))); + if (set.size() != 2) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing three elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3))); + if (set.size() != 3) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing four elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4))); + if (set.size() != 4) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing five elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5))); + if (set.size() != 5) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing six elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6))); + if (set.size() != 6) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing seven elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7))); + if (set.size() != 7) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing eight elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8))); + if (set.size() != 8) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing nine elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @param e9 the ninth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8), + Objects.requireNonNull(e9))); + if (set.size() != 9) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing ten elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @param e9 the ninth element + * @param e10 the tenth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8), + Objects.requireNonNull(e9), + Objects.requireNonNull(e10))); + if (set.size() != 10) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing an arbitrary number of elements. + * See Immutable Set Static Factory Methods for details. + * + * @apiNote + * This method also accepts a single array as an argument. The element type of + * the resulting set will be the component type of the array, and the size of + * the set will be equal to the length of the array. To create a set with + * a single element that is an array, do the following: + * + *

{@code
+     *     String[] array = ... ;
+     *     Set list = Set.of(array);
+     * }
+ * + * This will cause the {@link Set#of(Object) Set.of(E)} method + * to be invoked instead. + * + * @param the {@code Set}'s element type + * @param elements the elements to be contained in the set + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} or if the array is {@code null} + * + * @since 9 + */ + @SafeVarargs + static Set of(E... elements) { + for (E e : elements) { // throws NPE if es is null + Objects.requireNonNull(e); + } + @SuppressWarnings("varargs") + Set set = new HashSet<>(Arrays.asList(elements)); + if (set.size() != elements.length) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } } diff --git a/jdk/test/java/util/Collection/MOAT.java b/jdk/test/java/util/Collection/MOAT.java index 6a1165e9ba6..fe17c5034a3 100644 --- a/jdk/test/java/util/Collection/MOAT.java +++ b/jdk/test/java/util/Collection/MOAT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -58,6 +58,21 @@ import static java.util.Collections.*; import java.lang.reflect.*; public class MOAT { + // Collections under test must not be initialized to contain this value, + // and maps under test must not contain this value as a key. + // It's used as a sentinel for absent-element testing. + static final int ABSENT_VALUE = 778347983; + + static final Integer[] integerArray; + static { + Integer[] ia = new Integer[20]; + // fill with 1..20 inclusive + for (int i = 0; i < ia.length; i++) { + ia[i] = i + 1; + } + integerArray = ia; + } + public static void realMain(String[] args) { testCollection(new NewAbstractCollection()); @@ -178,6 +193,70 @@ public class MOAT { equal(singletonMap.size(), 1); testMap(singletonMap); testImmutableMap(singletonMap); + + // Immutable List + testEmptyList(List.of()); + for (List list : Arrays.asList( + List.of(), + List.of(1), + List.of(1, 2), + List.of(1, 2, 3), + List.of(1, 2, 3, 4), + List.of(1, 2, 3, 4, 5), + List.of(1, 2, 3, 4, 5, 6), + List.of(1, 2, 3, 4, 5, 6, 7), + List.of(1, 2, 3, 4, 5, 6, 7, 8), + List.of(1, 2, 3, 4, 5, 6, 7, 8, 9), + List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + List.of(integerArray))) { + testCollection(list); + testImmutableList(list); + } + + // Immutable Set + testEmptySet(Set.of()); + for (Set set : Arrays.asList( + Set.of(), + Set.of(1), + Set.of(1, 2), + Set.of(1, 2, 3), + Set.of(1, 2, 3, 4), + Set.of(1, 2, 3, 4, 5), + Set.of(1, 2, 3, 4, 5, 6), + Set.of(1, 2, 3, 4, 5, 6, 7), + Set.of(1, 2, 3, 4, 5, 6, 7, 8), + Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9), + Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + Set.of(integerArray))) { + testCollection(set); + testImmutableSet(set); + } + + // Immutable Map + + @SuppressWarnings("unchecked") + Map.Entry[] ea = (Map.Entry[])new Map.Entry[20]; + for (int i = 0; i < ea.length; i++) { + ea[i] = Map.entry(i+1, i+101); + } + + testEmptyMap(Map.of()); + for (Map map : Arrays.asList( + Map.of(), + Map.of(1, 101), + Map.of(1, 101, 2, 202), + Map.of(1, 101, 2, 202, 3, 303), + Map.of(1, 101, 2, 202, 3, 303, 4, 404), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707, 8, 808), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707, 8, 808, 9, 909), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707, 8, 808, 9, 909, 10, 1010), + Map.ofEntries(ea))) { + testMap(map); + testImmutableMap(map); + } } private static void checkContainsSelf(Collection c) { @@ -190,6 +269,17 @@ public class MOAT { check(c.containsAll(new ArrayList())); } + private static void checkUnique(Set s) { + for (Integer i : s) { + int count = 0; + for (Integer j : s) { + if (Objects.equals(i,j)) + ++count; + } + check(count == 1); + } + } + private static void testEmptyCollection(Collection c) { check(c.isEmpty()); equal(c.size(), 0); @@ -330,19 +420,19 @@ public class MOAT { } private static boolean supportsAdd(Collection c) { - try { check(c.add(778347983)); } + try { check(c.add(ABSENT_VALUE)); } catch (UnsupportedOperationException t) { return false; } catch (Throwable t) { unexpected(t); } try { - check(c.contains(778347983)); - check(c.remove(778347983)); + check(c.contains(ABSENT_VALUE)); + check(c.remove(ABSENT_VALUE)); } catch (Throwable t) { unexpected(t); } return true; } private static boolean supportsRemove(Collection c) { - try { check(! c.remove(19134032)); } + try { check(! c.remove(ABSENT_VALUE)); } catch (UnsupportedOperationException t) { return false; } catch (Throwable t) { unexpected(t); } return true; @@ -359,6 +449,7 @@ public class MOAT { checkContainsSelf(c); checkContainsEmpty(c); check(c.size() != 0 ^ c.isEmpty()); + check(! c.contains(ABSENT_VALUE)); { int size = 0; @@ -366,6 +457,10 @@ public class MOAT { check(c.size() == size); } + if (c instanceof Set) { + checkUnique((Set)c); + } + check(c.toArray().length == c.size()); check(c.toArray().getClass() == Object[].class || @@ -861,6 +956,20 @@ public class MOAT { checkFunctionalInvariants(m.keySet()); checkFunctionalInvariants(m.values()); check(m.size() != 0 ^ m.isEmpty()); + check(! m.containsKey(ABSENT_VALUE)); + + if (m instanceof Serializable) { + //System.out.printf("Serializing %s%n", m.getClass().getName()); + try { + Object clone = serialClone(m); + equal(m instanceof Serializable, + clone instanceof Serializable); + equal(m, clone); + } catch (Error xxx) { + if (! (xxx.getCause() instanceof NotSerializableException)) + throw xxx; + } + } } private static void testMap(Map m) { @@ -910,13 +1019,13 @@ public class MOAT { // We're asking for .equals(...) semantics if (m instanceof IdentityHashMap) return false; - try { check(m.put(778347983,12735) == null); } + try { check(m.put(ABSENT_VALUE,12735) == null); } catch (UnsupportedOperationException t) { return false; } catch (Throwable t) { unexpected(t); } try { - check(m.containsKey(778347983)); - check(m.remove(778347983) != null); + check(m.containsKey(ABSENT_VALUE)); + check(m.remove(ABSENT_VALUE) != null); } catch (Throwable t) { unexpected(t); } return true; } diff --git a/jdk/test/java/util/Collection/SetFactories.java b/jdk/test/java/util/Collection/SetFactories.java new file mode 100644 index 00000000000..aabeaa344a7 --- /dev/null +++ b/jdk/test/java/util/Collection/SetFactories.java @@ -0,0 +1,275 @@ +/* + * 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/* + * @test + * @bug 8048330 + * @summary Test convenience static factory methods on Set. + * @run testng SetFactories + */ + + +public class SetFactories { + + static final int NUM_STRINGS = 20; // should be larger than the largest fixed-arg overload + static final String[] stringArray; + static { + String[] sa = new String[NUM_STRINGS]; + for (int i = 0; i < NUM_STRINGS; i++) { + sa[i] = String.valueOf((char)('a' + i)); + } + stringArray = sa; + } + + static Object[] a(Set act, Set exp) { + return new Object[] { act, exp }; + } + + static Set hashSetOf(String... args) { + return new HashSet<>(Arrays.asList(args)); + } + + @DataProvider(name="empty") + public Iterator empty() { + return Collections.singletonList( + // actual, expected + a(Set.of(), Collections.emptySet()) + ).iterator(); + } + + @DataProvider(name="nonempty") + public Iterator nonempty() { + return Arrays.asList( + // actual, expected + a( Set.of("a"), + hashSetOf("a")), + a( Set.of("a", "b"), + hashSetOf("a", "b")), + a( Set.of("a", "b", "c"), + hashSetOf("a", "b", "c")), + a( Set.of("a", "b", "c", "d"), + hashSetOf("a", "b", "c", "d")), + a( Set.of("a", "b", "c", "d", "e"), + hashSetOf("a", "b", "c", "d", "e")), + a( Set.of("a", "b", "c", "d", "e", "f"), + hashSetOf("a", "b", "c", "d", "e", "f")), + a( Set.of("a", "b", "c", "d", "e", "f", "g"), + hashSetOf("a", "b", "c", "d", "e", "f", "g")), + a( Set.of("a", "b", "c", "d", "e", "f", "g", "h"), + hashSetOf("a", "b", "c", "d", "e", "f", "g", "h")), + a( Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i"), + hashSetOf("a", "b", "c", "d", "e", "f", "g", "h", "i")), + a( Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), + hashSetOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")), + a( Set.of(stringArray), + hashSetOf(stringArray)) + ).iterator(); + } + + @DataProvider(name="all") + public Iterator all() { + List all = new ArrayList<>(); + empty().forEachRemaining(all::add); + nonempty().forEachRemaining(all::add); + return all.iterator(); + } + + @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class) + public void cannotAdd(Set act, Set exp) { + act.add("x"); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotRemove(Set act, Set exp) { + act.remove(act.iterator().next()); + } + + @Test(dataProvider="all") + public void contentsMatch(Set act, Set exp) { + assertEquals(act, exp); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed2() { + Set set = Set.of("a", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed3() { + Set set = Set.of("a", "b", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed4() { + Set set = Set.of("a", "b", "c", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed5() { + Set set = Set.of("a", "b", "c", "d", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed6() { + Set set = Set.of("a", "b", "c", "d", "e", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed7() { + Set set = Set.of("a", "b", "c", "d", "e", "f", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed8() { + Set set = Set.of("a", "b", "c", "d", "e", "f", "g", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed9() { + Set set = Set.of("a", "b", "c", "d", "e", "f", "g", "h", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed10() { + Set set = Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowedN() { + String[] array = stringArray.clone(); + array[0] = array[1]; + Set set = Set.of(array); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed1() { + Set.of((String)null); // force one-arg overload + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed2a() { + Set.of("a", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed2b() { + Set.of(null, "b"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed3() { + Set.of("a", "b", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed4() { + Set.of("a", "b", "c", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed5() { + Set.of("a", "b", "c", "d", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed6() { + Set.of("a", "b", "c", "d", "e", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed7() { + Set.of("a", "b", "c", "d", "e", "f", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed8() { + Set.of("a", "b", "c", "d", "e", "f", "g", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed9() { + Set.of("a", "b", "c", "d", "e", "f", "g", "h", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed10() { + Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowedN() { + String[] array = stringArray.clone(); + array[0] = null; + Set.of(array); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullArrayDisallowed() { + Set.of((Object[])null); + } + + @Test(dataProvider="all") + public void serialEquality(Set act, Set exp) { + // assume that act.equals(exp) tested elsewhere + Set copy = serialClone(act); + assertEquals(act, copy); + assertEquals(copy, exp); + } + + @SuppressWarnings("unchecked") + static T serialClone(T obj) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + return (T) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new AssertionError(e); + } + } +} diff --git a/jdk/test/java/util/List/ListFactories.java b/jdk/test/java/util/List/ListFactories.java new file mode 100644 index 00000000000..e34ca660705 --- /dev/null +++ b/jdk/test/java/util/List/ListFactories.java @@ -0,0 +1,234 @@ +/* + * 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static java.util.Arrays.asList; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/* + * @test + * @bug 8048330 + * @summary Test convenience static factory methods on List. + * @run testng ListFactories + */ + +public class ListFactories { + + static final int NUM_STRINGS = 20; // should be larger than the largest fixed-arg overload + static final String[] stringArray; + static { + String[] sa = new String[NUM_STRINGS]; + for (int i = 0; i < NUM_STRINGS; i++) { + sa[i] = String.valueOf((char)('a' + i)); + } + stringArray = sa; + } + + // returns array of [actual, expected] + static Object[] a(List act, List exp) { + return new Object[] { act, exp }; + } + + @DataProvider(name="empty") + public Iterator empty() { + return Collections.singletonList( + a(List.of(), Collections.emptyList()) + ).iterator(); + } + + @DataProvider(name="nonempty") + public Iterator nonempty() { + return asList( + a(List.of("a"), + asList("a")), + a(List.of("a", "b"), + asList("a", "b")), + a(List.of("a", "b", "c"), + asList("a", "b", "c")), + a(List.of("a", "b", "c", "d"), + asList("a", "b", "c", "d")), + a(List.of("a", "b", "c", "d", "e"), + asList("a", "b", "c", "d", "e")), + a(List.of("a", "b", "c", "d", "e", "f"), + asList("a", "b", "c", "d", "e", "f")), + a(List.of("a", "b", "c", "d", "e", "f", "g"), + asList("a", "b", "c", "d", "e", "f", "g")), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h"), + asList("a", "b", "c", "d", "e", "f", "g", "h")), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i"), + asList("a", "b", "c", "d", "e", "f", "g", "h", "i")), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), + asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")), + a(List.of(stringArray), + asList(stringArray)) + ).iterator(); + } + + @DataProvider(name="all") + public Iterator all() { + List all = new ArrayList<>(); + empty().forEachRemaining(all::add); + nonempty().forEachRemaining(all::add); + return all.iterator(); + } + + @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class) + public void cannotAddLast(List act, List exp) { + act.add("x"); + } + + @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class) + public void cannotAddFirst(List act, List exp) { + act.add(0, "x"); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotRemove(List act, List exp) { + act.remove(0); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotSet(List act, List exp) { + act.set(0, "x"); + } + + @Test(dataProvider="all") + public void contentsMatch(List act, List exp) { + assertEquals(act, exp); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed1() { + List.of((Object)null); // force one-arg overload + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed2a() { + List.of("a", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed2b() { + List.of(null, "b"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed3() { + List.of("a", "b", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed4() { + List.of("a", "b", "c", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed5() { + List.of("a", "b", "c", "d", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed6() { + List.of("a", "b", "c", "d", "e", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed7() { + List.of("a", "b", "c", "d", "e", "f", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed8() { + List.of("a", "b", "c", "d", "e", "f", "g", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed9() { + List.of("a", "b", "c", "d", "e", "f", "g", "h", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed10() { + List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowedN() { + String[] array = stringArray.clone(); + array[0] = null; + List.of(array); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullArrayDisallowed() { + List.of((Object[])null); + } + + @Test + public void ensureArrayCannotModifyList() { + String[] array = stringArray.clone(); + List list = List.of(array); + array[0] = "xyzzy"; + assertEquals(list, Arrays.asList(stringArray)); + } + + @Test(dataProvider="all") + public void serialEquality(List act, List exp) { + // assume that act.equals(exp) tested elsewhere + List copy = serialClone(act); + assertEquals(act, copy); + assertEquals(copy, exp); + } + + @SuppressWarnings("unchecked") + static T serialClone(T obj) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + return (T) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new AssertionError(e); + } + } +} diff --git a/jdk/test/java/util/Map/MapFactories.java b/jdk/test/java/util/Map/MapFactories.java new file mode 100644 index 00000000000..1bdb020680d --- /dev/null +++ b/jdk/test/java/util/Map/MapFactories.java @@ -0,0 +1,380 @@ +/* + * 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/* + * @test + * @bug 8048330 + * @summary Test convenience static factory methods on Map. + * @run testng MapFactories + */ + +public class MapFactories { + + static final int MAX_ENTRIES = 20; // should be larger than the largest fixed-arg overload + static String valueFor(int i) { + // the String literal below should be of length MAX_ENTRIES + return "abcdefghijklmnopqrst".substring(i, i+1); + } + + // for "expected" values + Map genMap(int n) { + Map result = new HashMap<>(); + for (int i = 0; i < n; i++) { + result.put(i, valueFor(i)); + } + return result; + } + + // for varargs Map.Entry methods + @SuppressWarnings("unchecked") + Map.Entry[] genEntries(int n) { + return IntStream.range(0, n) + .mapToObj(i -> Map.entry(i, valueFor(i))) + .toArray(Map.Entry[]::new); + } + + // returns array of [actual, expected] + static Object[] a(Map act, Map exp) { + return new Object[] { act, exp }; + } + + @DataProvider(name="empty") + public Iterator empty() { + return Collections.singletonList( + a(Map.of(), genMap(0)) + ).iterator(); + } + + @DataProvider(name="nonempty") + @SuppressWarnings("unchecked") + public Iterator nonempty() { + return Arrays.asList( + a(Map.of(0, "a"), genMap(1)), + a(Map.of(0, "a", 1, "b"), genMap(2)), + a(Map.of(0, "a", 1, "b", 2, "c"), genMap(3)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d"), genMap(4)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e"), genMap(5)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f"), genMap(6)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g"), genMap(7)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h"), genMap(8)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i"), genMap(9)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i", 9, "j"), genMap(10)), + a(Map.ofEntries(genEntries(MAX_ENTRIES)), genMap(MAX_ENTRIES)) + ).iterator(); + } + + @DataProvider(name="all") + public Iterator all() { + List all = new ArrayList<>(); + empty().forEachRemaining(all::add); + nonempty().forEachRemaining(all::add); + return all.iterator(); + } + + @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class) + public void cannotPutNew(Map act, Map exp) { + act.put(-1, "xyzzy"); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotPutOld(Map act, Map exp) { + act.put(0, "a"); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotRemove(Map act, Map exp) { + act.remove(act.keySet().iterator().next()); + } + + @Test(dataProvider="all") + public void contentsMatch(Map act, Map exp) { + assertEquals(act, exp); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed2() { + Map map = Map.of(0, "a", 0, "b"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed3() { + Map map = Map.of(0, "a", 1, "b", 0, "c"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed4() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 0, "d"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed5() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 0, "e"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed6() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 0, "f"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed7() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 0, "g"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed8() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 0, "h"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed9() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 0, "i"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed10() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 8, "i", 0, "j"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowedN() { + Map.Entry[] entries = genEntries(MAX_ENTRIES); + entries[MAX_ENTRIES-1] = Map.entry(0, "xxx"); + Map map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed1() { + Map map = Map.of(null, "a"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed1() { + Map map = Map.of(0, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed2() { + Map map = Map.of(0, "a", null, "b"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed2() { + Map map = Map.of(0, "a", 1, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed3() { + Map map = Map.of(0, "a", 1, "b", null, "c"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed3() { + Map map = Map.of(0, "a", 1, "b", 2, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed4() { + Map map = Map.of(0, "a", 1, "b", 2, "c", null, "d"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed4() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed5() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", null, "e"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed5() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed6() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + null, "f"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed6() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed7() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", null, "g"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed7() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed8() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", null, "h"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed8() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed9() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", null, "i"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed9() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 8, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed10() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 8, "i", null, "j"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed10() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 8, "i", 9, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowedN() { + Map.Entry[] entries = genEntries(MAX_ENTRIES); + entries[0] = new AbstractMap.SimpleImmutableEntry(null, "a"); + Map map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowedN() { + Map.Entry[] entries = genEntries(MAX_ENTRIES); + entries[0] = new AbstractMap.SimpleImmutableEntry(0, null); + Map map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullEntryDisallowedN() { + Map.Entry[] entries = genEntries(MAX_ENTRIES); + entries[5] = null; + Map map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullArrayDisallowed() { + Map.ofEntries(null); + } + + @Test(dataProvider="all") + public void serialEquality(Map act, Map exp) { + // assume that act.equals(exp) tested elsewhere + Map copy = serialClone(act); + assertEquals(act, copy); + assertEquals(copy, exp); + } + + @SuppressWarnings("unchecked") + static T serialClone(T obj) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + return (T) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new AssertionError(e); + } + } + + // Map.entry() tests + + @Test(expectedExceptions=NullPointerException.class) + public void entryWithNullKeyDisallowed() { + Map.Entry e = Map.entry(null, "x"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void entryWithNullValueDisallowed() { + Map.Entry e = Map.entry(0, null); + } + + @Test + public void entryBasicTests() { + Map.Entry kvh1 = Map.entry("xyzzy", "plugh"); + Map.Entry kvh2 = Map.entry("foobar", "blurfl"); + Map.Entry sie = new AbstractMap.SimpleImmutableEntry("xyzzy", "plugh"); + + assertTrue(kvh1.equals(sie)); + assertTrue(sie.equals(kvh1)); + assertFalse(kvh2.equals(sie)); + assertFalse(sie.equals(kvh2)); + assertEquals(sie.hashCode(), kvh1.hashCode()); + assertEquals(sie.toString(), kvh1.toString()); + } + +} From 30655cc742f0faffb01cfb996ad0d243baac078a Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Wed, 9 Dec 2015 10:36:33 +0000 Subject: [PATCH 016/199] 8141651: Deadlock in sun.security.ssl.SSLSocketImpl Reviewed-by: weijun --- .../classes/sun/security/ssl/SSLSocketImpl.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index e3919c5d043..0162dc3b477 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -766,17 +766,27 @@ public final class SSLSocketImpl extends BaseSSLSocketImpl { // records, so this also increases robustness. // if (length > 0) { + IOException ioe = null; + byte description = 0; // 0: never used, make the compiler happy writeLock.lock(); try { outputRecord.deliver(source, offset, length); } catch (SSLHandshakeException she) { // may be record sequence number overflow - fatal(Alerts.alert_handshake_failure, she); + description = Alerts.alert_handshake_failure; + ioe = she; } catch (IOException e) { - fatal(Alerts.alert_unexpected_message, e); + description = Alerts.alert_unexpected_message; + ioe = e; } finally { writeLock.unlock(); } + + // Be care of deadlock. Please don't place the call to fatal() + // into the writeLock locked block. + if (ioe != null) { + fatal(description, ioe); + } } /* From 6af208a48532af66103cec241250426ee12f8483 Mon Sep 17 00:00:00 2001 From: Rob McKenna Date: Wed, 9 Dec 2015 15:16:00 +0000 Subject: [PATCH 017/199] 8143397: It looks like InetAddress.isReachable(timeout) works incorrectly Reviewed-by: xuelei, msheppar --- .../windows/native/libnet/Inet4AddressImpl.c | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c b/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c index 5213383c1e5..8f70ebe0c12 100644 --- a/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c +++ b/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c @@ -295,6 +295,7 @@ ping4(JNIEnv *env, char SendData[32] = {0}; LPVOID ReplyBuffer = NULL; DWORD ReplySize = 0; + jboolean ret = JNI_FALSE; hIcmpFile = IcmpCreateFile(); if (hIcmpFile == INVALID_HANDLE_VALUE) { @@ -318,7 +319,11 @@ ping4(JNIEnv *env, NULL, // PIP_OPTION_INFORMATION RequestOptions, ReplyBuffer,// LPVOID ReplyBuffer, ReplySize, // DWORD ReplySize, - timeout); // DWORD Timeout + // Note: IcmpSendEcho and its derivatives + // seem to have an undocumented minimum + // timeout of 1000ms below which the + // api behaves inconsistently. + (timeout < 1000) ? 1000 : timeout); // DWORD Timeout } else { dwRetVal = IcmpSendEcho2Ex(hIcmpFile, // HANDLE IcmpHandle, NULL, // HANDLE Event @@ -331,17 +336,19 @@ ping4(JNIEnv *env, NULL, // PIP_OPTION_INFORMATION RequestOptions, ReplyBuffer,// LPVOID ReplyBuffer, ReplySize, // DWORD ReplySize, - timeout); // DWORD Timeout + (timeout < 1000) ? 1000 : timeout); // DWORD Timeout + } + + if (dwRetVal != 0) { + PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer; + if ((int)pEchoReply->RoundTripTime <= timeout) + ret = JNI_TRUE; } free(ReplyBuffer); IcmpCloseHandle(hIcmpFile); - if (dwRetVal != 0) { - return JNI_TRUE; - } else { - return JNI_FALSE; - } + return ret; } /* From d37bb2422b487bc19e58bf5642a8fbbea4825d5f Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Wed, 9 Dec 2015 18:25:36 +0100 Subject: [PATCH 018/199] 8143127: InvokerBytecodeGenerator emitConst should handle Byte, Short, Character Reviewed-by: vlivanov, shade, forax --- .../lang/invoke/InvokerBytecodeGenerator.java | 73 ++++++++++++------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index 83d66d52299..08c53447a80 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -324,27 +324,54 @@ class InvokerBytecodeGenerator { emitIconstInsn((int) con); return; } + if (con instanceof Byte) { + emitIconstInsn((byte)con); + return; + } + if (con instanceof Short) { + emitIconstInsn((short)con); + return; + } + if (con instanceof Character) { + emitIconstInsn((char)con); + return; + } if (con instanceof Long) { long x = (long) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2L); + short sx = (short)x; + if (x == sx) { + if (sx >= 0 && sx <= 1) { + mv.visitInsn(Opcodes.LCONST_0 + (int) sx); + } else { + emitIconstInsn((int) x); + mv.visitInsn(Opcodes.I2L); + } return; } } if (con instanceof Float) { float x = (float) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2F); + short sx = (short)x; + if (x == sx) { + if (sx >= 0 && sx <= 2) { + mv.visitInsn(Opcodes.FCONST_0 + (int) sx); + } else { + emitIconstInsn((int) x); + mv.visitInsn(Opcodes.I2F); + } return; } } if (con instanceof Double) { double x = (double) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2D); + short sx = (short)x; + if (x == sx) { + if (sx >= 0 && sx <= 1) { + mv.visitInsn(Opcodes.DCONST_0 + (int) sx); + } else { + emitIconstInsn((int) x); + mv.visitInsn(Opcodes.I2D); + } return; } } @@ -356,26 +383,16 @@ class InvokerBytecodeGenerator { mv.visitLdcInsn(con); } - private void emitIconstInsn(int i) { - int opcode; - switch (i) { - case 0: opcode = Opcodes.ICONST_0; break; - case 1: opcode = Opcodes.ICONST_1; break; - case 2: opcode = Opcodes.ICONST_2; break; - case 3: opcode = Opcodes.ICONST_3; break; - case 4: opcode = Opcodes.ICONST_4; break; - case 5: opcode = Opcodes.ICONST_5; break; - default: - if (i == (byte) i) { - mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF); - } else if (i == (short) i) { - mv.visitIntInsn(Opcodes.SIPUSH, (char) i); - } else { - mv.visitLdcInsn(i); - } - return; + private void emitIconstInsn(final int cst) { + if (cst >= -1 && cst <= 5) { + mv.visitInsn(Opcodes.ICONST_0 + cst); + } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { + mv.visitIntInsn(Opcodes.BIPUSH, cst); + } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { + mv.visitIntInsn(Opcodes.SIPUSH, cst); + } else { + mv.visitLdcInsn(cst); } - mv.visitInsn(opcode); } /* From 8da753e1be0d4de4cbb71b80a85668c4a141a4ea Mon Sep 17 00:00:00 2001 From: Rob McKenna Date: Wed, 9 Dec 2015 17:34:09 +0000 Subject: [PATCH 019/199] 8141370: com/sun/jndi/ldap/LdapTimeoutTest.java failed intermittently Reviewed-by: vinnie --- jdk/test/ProblemList.txt | 7 + .../sun/jndi/ldap/DeadSSLLdapTimeoutTest.java | 210 ++++++++++++++++++ .../com/sun/jndi/ldap/LdapTimeoutTest.java | 38 ---- 3 files changed, 217 insertions(+), 38 deletions(-) create mode 100644 jdk/test/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 8d61ebc654e..51abaacbf53 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -407,3 +407,10 @@ sun/tools/jinfo/JInfoRunningProcessFlagTest.java generic-all sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java generic-all ############################################################################ + +# jdk_other + +############################################################################ + +# 8141370 +com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java linux-i586,macosx-all diff --git a/jdk/test/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java b/jdk/test/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java new file mode 100644 index 00000000000..1301c4c2466 --- /dev/null +++ b/jdk/test/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2011, 2014, 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. + */ + +/** + * @test + * @run main/othervm DeadSSLLdapTimeoutTest + * @bug 8141370 + * @key intermittent + */ + +import java.net.Socket; +import java.net.ServerSocket; +import java.net.SocketTimeoutException; +import java.io.*; +import javax.naming.*; +import javax.naming.directory.*; +import java.util.List; +import java.util.Hashtable; +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLHandshakeException; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + + +class DeadServerTimeoutSSLTest implements Callable { + + Hashtable env; + DeadSSLServer server; + boolean passed = false; + private int HANGING_TEST_TIMEOUT = 20_000; + + public DeadServerTimeoutSSLTest(Hashtable env) throws IOException { + this.server = new DeadSSLServer(); + this.env = env; + } + + public void performOp(InitialContext ctx) throws NamingException {} + + public void handleNamingException(NamingException e, long start, long end) { + if (e.getCause() instanceof SocketTimeoutException) { + // SSL connect will timeout via readReply using + // SocketTimeoutException + e.printStackTrace(); + pass(); + } else if (e.getCause() instanceof SSLHandshakeException + && e.getCause().getCause() instanceof EOFException) { + // test seems to be failing intermittently on some + // platforms. + pass(); + } else { + fail(e); + } + } + + public void pass() { + this.passed = true; + } + + public void fail() { + throw new RuntimeException("Test failed"); + } + + public void fail(Exception e) { + throw new RuntimeException("Test failed", e); + } + + boolean shutItDown(InitialContext ctx) { + try { + if (ctx != null) ctx.close(); + return true; + } catch (NamingException ex) { + return false; + } + } + + public Boolean call() { + InitialContext ctx = null; + ScheduledFuture killer = null; + long start = System.nanoTime(); + + try { + while(!server.accepting()) + Thread.sleep(200); // allow the server to start up + Thread.sleep(200); // to be sure + + env.put(Context.PROVIDER_URL, "ldap://localhost:" + + server.getLocalPort()); + + try { + ctx = new InitialDirContext(env); + performOp(ctx); + fail(); + } catch (NamingException e) { + long end = System.nanoTime(); + System.out.println(this.getClass().toString() + " - elapsed: " + + NANOSECONDS.toMillis(end - start)); + handleNamingException(e, start, end); + } finally { + if (killer != null && !killer.isDone()) + killer.cancel(true); + shutItDown(ctx); + server.close(); + } + return passed; + } catch (IOException|InterruptedException e) { + throw new RuntimeException(e); + } + } +} + +class DeadSSLServer extends Thread { + ServerSocket serverSock; + boolean accepting = false; + + public DeadSSLServer() throws IOException { + this.serverSock = new ServerSocket(0); + start(); + } + + public void run() { + while(true) { + try { + accepting = true; + Socket socket = serverSock.accept(); + } catch (Exception e) { + break; + } + } + } + + public int getLocalPort() { + return serverSock.getLocalPort(); + } + + public boolean accepting() { + return accepting; + } + + public void close() throws IOException { + serverSock.close(); + } +} + +public class DeadSSLLdapTimeoutTest { + + static Hashtable createEnv() { + Hashtable env = new Hashtable(11); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + return env; + } + + public static void main(String[] args) throws Exception { + + InitialContext ctx = null; + + // + // Running this test serially as it seems to tickle a problem + // on older kernels + // + // run the DeadServerTest with connect / read timeouts set + // and ssl enabled + // this should exit with a SocketTimeoutException as the root cause + // it should also use the connect timeout instead of the read timeout + System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout & SSL"); + Hashtable sslenv = createEnv(); + sslenv.put("com.sun.jndi.ldap.connect.timeout", "10"); + sslenv.put("com.sun.jndi.ldap.read.timeout", "3000"); + sslenv.put(Context.SECURITY_PROTOCOL, "ssl"); + boolean testFailed = + (new DeadServerTimeoutSSLTest(sslenv).call()) ? false : true; + + if (testFailed) { + throw new AssertionError("some tests failed"); + } + + } + +} + diff --git a/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java b/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java index fe48073a6b3..5da62ee647e 100644 --- a/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java +++ b/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java @@ -225,29 +225,6 @@ class DeadServerTimeoutTest extends DeadServerTest { } } -class DeadServerTimeoutSSLTest extends DeadServerTest { - - public DeadServerTimeoutSSLTest(Hashtable env) throws IOException { - super(env); - } - - public void handleNamingException(NamingException e, long start, long end) { - if (e.getCause() instanceof SocketTimeoutException) { - // SSL connect will timeout via readReply using - // SocketTimeoutException - e.printStackTrace(); - pass(); - } else if (e.getCause() instanceof SSLHandshakeException - && e.getCause().getCause() instanceof EOFException) { - // test seems to be failing intermittently on some - // platforms. - pass(); - } else { - fail(e); - } - } -} - class ReadServerNoTimeoutTest extends ReadServerTest { @@ -454,21 +431,6 @@ public class LdapTimeoutTest { } } - // - // Running this test serially as it seems to tickle a problem - // on older kernels - // - // run the DeadServerTest with connect / read timeouts set - // and ssl enabled - // this should exit with a SocketTimeoutException as the root cause - // it should also use the connect timeout instead of the read timeout - System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout & SSL"); - Hashtable sslenv = createEnv(); - sslenv.put("com.sun.jndi.ldap.connect.timeout", "10"); - sslenv.put("com.sun.jndi.ldap.read.timeout", "3000"); - sslenv.put(Context.SECURITY_PROTOCOL, "ssl"); - testFailed = (new DeadServerTimeoutSSLTest(sslenv).call()) ? false : true; - if (testFailed) { throw new AssertionError("some tests failed"); } From ee046050d6aaa2aaef4b79d3ae70becff8a0bc5b Mon Sep 17 00:00:00 2001 From: Rachna Goel Date: Wed, 9 Dec 2015 14:20:51 +0530 Subject: [PATCH 020/199] 8025547: Locale.toString() documentation error Updated API doc of Locale.toString method. Reviewed-by: okutsu --- jdk/src/java.base/share/classes/java/util/Locale.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/util/Locale.java b/jdk/src/java.base/share/classes/java/util/Locale.java index 0a7c02965f6..d01891be0c5 100644 --- a/jdk/src/java.base/share/classes/java/util/Locale.java +++ b/jdk/src/java.base/share/classes/java/util/Locale.java @@ -1248,7 +1248,7 @@ public final class Locale implements Cloneable, Serializable { * object, consisting of language, country, variant, script, * and extensions as below: *
- * language + "_" + country + "_" + (variant + "_#" | "#") + script + "-" + extensions + * language + "_" + country + "_" + (variant + "_#" | "#") + script + "_" + extensions *
* * Language is always lower case, country is always upper case, script is always title @@ -1278,7 +1278,7 @@ public final class Locale implements Cloneable, Serializable { *
  • {@code en_US_WIN}
  • *
  • {@code de__POSIX}
  • *
  • {@code zh_CN_#Hans}
  • - *
  • {@code zh_TW_#Hant-x-java}
  • + *
  • {@code zh_TW_#Hant_x-java}
  • *
  • {@code th_TH_TH_#u-nu-thai}
  • * * @return A string representation of the Locale, for debugging. From 7d2c07c70d55d4fc692f18cfec0c9c0634b26e0b Mon Sep 17 00:00:00 2001 From: Nadeesh TV Date: Wed, 9 Dec 2015 15:27:21 -0500 Subject: [PATCH 021/199] 8142936: Add java.time.Duration methods for days, hours, minutes, seconds, etc Reviewed-by: rriggs, scolebourne --- .../share/classes/java/time/Duration.java | 118 ++++++++++++++- .../java/time/tck/java/time/TCKDuration.java | 141 ++++++++++++++++++ 2 files changed, 254 insertions(+), 5 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/time/Duration.java b/jdk/src/java.base/share/classes/java/time/Duration.java index 9bf25ba8c27..b7060a91ef4 100644 --- a/jdk/src/java.base/share/classes/java/time/Duration.java +++ b/jdk/src/java.base/share/classes/java/time/Duration.java @@ -61,6 +61,7 @@ */ package java.time; +import static java.time.LocalTime.MINUTES_PER_HOUR; import static java.time.LocalTime.NANOS_PER_SECOND; import static java.time.LocalTime.SECONDS_PER_DAY; import static java.time.LocalTime.SECONDS_PER_HOUR; @@ -973,7 +974,7 @@ public final class Duration if (multiplicand == 1) { return this; } - return create(toSeconds().multiply(BigDecimal.valueOf(multiplicand))); + return create(toBigDecimalSeconds().multiply(BigDecimal.valueOf(multiplicand))); } /** @@ -992,7 +993,7 @@ public final class Duration if (divisor == 1) { return this; } - return create(toSeconds().divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN)); + return create(toBigDecimalSeconds().divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN)); } /** @@ -1001,7 +1002,7 @@ public final class Duration * * @return the total length of the duration in seconds, with a scale of 9, not null */ - private BigDecimal toSeconds() { + private BigDecimal toBigDecimalSeconds() { return BigDecimal.valueOf(seconds).add(BigDecimal.valueOf(nanos, 9)); } @@ -1167,6 +1168,19 @@ public final class Duration return seconds / SECONDS_PER_MINUTE; } + /** + * Gets the number of seconds in this duration. + *

    + * This returns the total number of whole seconds in the duration. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the whole seconds part of the length of the duration, positive or negative + */ + public long toSeconds() { + return seconds; + } + /** * Converts this duration to the total length in milliseconds. *

    @@ -1201,6 +1215,100 @@ public final class Duration return totalNanos; } + /** + * Extracts the number of days in the duration. + *

    + * This returns the total number of days in the duration by dividing the + * number of seconds by 86400. + * This is based on the standard definition of a day as 24 hours. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of days in the duration, may be negative + */ + public long toDaysPart(){ + return seconds / SECONDS_PER_DAY; + } + + /** + * Extracts the number of hours part in the duration. + *

    + * This returns the number of remaining hours when dividing {@link #toHours} + * by hours in a day. + * This is based on the standard definition of a day as 24 hours. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of hours part in the duration, may be negative + */ + public int toHoursPart(){ + return (int) (toHours() % 24); + } + + /** + * Extracts the number of minutes part in the duration. + *

    + * This returns the number of remaining minutes when dividing {@link #toMinutes} + * by minutes in an hour. + * This is based on the standard definition of an hour as 60 minutes. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of minutes parts in the duration, may be negative + * may be negative + */ + public int toMinutesPart(){ + return (int) (toMinutes() % MINUTES_PER_HOUR); + } + + /** + * Extracts the number of seconds part in the duration. + *

    + * This returns the remaining seconds when dividing {@link #toSeconds} + * by seconds in a minute. + * This is based on the standard definition of a minute as 60 seconds. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of seconds parts in the duration, may be negative + */ + public int toSecondsPart(){ + return (int) (seconds % SECONDS_PER_MINUTE); + } + + /** + * Extracts the number of milliseconds part of the duration. + *

    + * This returns the milliseconds part by dividing the number of nanoseconds by 1,000,000. + * The length of the duration is stored using two fields - seconds and nanoseconds. + * The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to + * the length in seconds. + * The total duration is defined by calling {@link #getNano()} and {@link #getSeconds()}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of milliseconds part of the duration. + */ + public int toMillisPart(){ + return nanos / 1000_000; + } + + /** + * Get the nanoseconds part within seconds of the duration. + *

    + * The length of the duration is stored using two fields - seconds and nanoseconds. + * The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to + * the length in seconds. + * The total duration is defined by calling {@link #getNano()} and {@link #getSeconds()}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the nanoseconds within the second part of the length of the duration, from 0 to 999,999,999 + */ + public int toNanosPart(){ + return nanos; + } + //----------------------------------------------------------------------- /** * Compares this duration to the specified {@code Duration}. @@ -1208,7 +1316,7 @@ public final class Duration * The comparison is based on the total length of the durations. * It is "consistent with equals", as defined by {@link Comparable}. * - * @param otherDuration the other duration to compare to, not null + * @param otherDuration the other duration to compare to, not null * @return the comparator value, negative if less, positive if greater */ @Override @@ -1226,7 +1334,7 @@ public final class Duration *

    * The comparison is based on the total length of the durations. * - * @param otherDuration the other duration, null returns false + * @param otherDuration the other duration, null returns false * @return true if the other duration is equal to this one */ @Override diff --git a/jdk/test/java/time/tck/java/time/TCKDuration.java b/jdk/test/java/time/tck/java/time/TCKDuration.java index fe6a9f98088..7f70916ac95 100644 --- a/jdk/test/java/time/tck/java/time/TCKDuration.java +++ b/jdk/test/java/time/tck/java/time/TCKDuration.java @@ -2474,6 +2474,147 @@ public class TCKDuration extends AbstractTCKTest { test.toMillis(); } + //----------------------------------------------------------------------- + // toSeconds() + //----------------------------------------------------------------------- + @DataProvider(name="toSeconds_provider") + Object[][] provider_toSeconds() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 31556926L}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -31556927L}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, 123_456_789), -31556926L}, + {Duration.ofSeconds(0), 0L}, + {Duration.ofSeconds(0, 123_456_789), 0L}, + {Duration.ofSeconds(0, -123_456_789), -1L}, + {Duration.ofSeconds(Long.MAX_VALUE), 9223372036854775807L}, + {Duration.ofSeconds(Long.MIN_VALUE), -9223372036854775808L}, + }; + } + + @Test(dataProvider="toSeconds_provider") + public void test_toSeconds(Duration dur, long seconds) { + assertEquals(dur.toSeconds(), seconds); + } + + //----------------------------------------------------------------------- + // toDaysPart() + //----------------------------------------------------------------------- + @DataProvider(name="toDaysPart_provider") + Object[][] provider_toDaysPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 365L}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -365L}, + {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 123_456_789), 0L}, + {Duration.ofDays(365), 365L}, + {Duration.ofHours(2), 0L}, + {Duration.ofHours(-2), 0L}, + }; + } + + @Test(dataProvider="toDaysPart_provider") + public void test_toDaysPart(Duration dur, long days) { + assertEquals(dur.toDaysPart(), days); + } + + //----------------------------------------------------------------------- + // toHoursPart() + //----------------------------------------------------------------------- + @DataProvider(name="toHoursPart_provider") + Object[][] provider_toHoursPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 5}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -5}, + {Duration.ofSeconds(48 * 60 + 46, 123_456_789), 0}, + {Duration.ofHours(2), 2}, + {Duration.ofHours(-2), -2}, + }; + } + + @Test(dataProvider="toHoursPart_provider") + public void test_toHoursPart(Duration dur, int hours) { + assertEquals(dur.toHoursPart(), hours); + } + + //----------------------------------------------------------------------- + // toMinutesPart() + //----------------------------------------------------------------------- + @DataProvider(name="toMinutesPart_provider") + Object[][] provider_toMinutesPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 48}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -48}, + {Duration.ofSeconds(46, 123_456_789),0}, + {Duration.ofMinutes(48), 48}, + {Duration.ofHours(2), 0}, + {Duration.ofHours(-2),0}, + }; + } + + @Test(dataProvider="toMinutesPart_provider") + public void test_toMinutesPart(Duration dur, int minutes) { + assertEquals(dur.toMinutesPart(), minutes); + } + + //----------------------------------------------------------------------- + // toSecondsPart() + //----------------------------------------------------------------------- + @DataProvider(name="toSecondsPart_provider") + Object[][] provider_toSecondsPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 46}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -47}, + {Duration.ofSeconds(0, 123_456_789), 0}, + {Duration.ofSeconds(46), 46}, + {Duration.ofHours(2), 0}, + {Duration.ofHours(-2), 0}, + }; + } + + @Test(dataProvider="toSecondsPart_provider") + public void test_toSecondsPart(Duration dur, int seconds) { + assertEquals(dur.toSecondsPart(), seconds); + } + + //----------------------------------------------------------------------- + // toMillisPart() + //----------------------------------------------------------------------- + @DataProvider(name="toMillisPart_provider") + Object[][] provider_toMillisPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 123}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), 876}, + {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 0), 0}, + {Duration.ofMillis(123), 123}, + {Duration.ofHours(2), 0}, + {Duration.ofHours(-2), 0}, + }; + } + + @Test(dataProvider="toMillisPart_provider") + public void test_toMillisPart(Duration dur, int millis) { + assertEquals(dur.toMillisPart(), millis); + } + + //----------------------------------------------------------------------- + // toNanosPart() + //----------------------------------------------------------------------- + @DataProvider(name="toNanosPart_provider") + Object[][] provider_toNanosPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 123_456_789}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), 876_543_211}, + {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 0), 0}, + {Duration.ofNanos(123_456_789), 123_456_789}, + {Duration.ofHours(2), 0}, + {Duration.ofHours(-2), 0}, + }; + } + + @Test(dataProvider="toNanosPart_provider") + public void test_toNanosPart(Duration dur, int nanos) { + assertEquals(dur.toNanosPart(), nanos); + } + //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- From e03cf9daf6d006a077fa3ee30c5bac5c6e9d91bf Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Thu, 10 Dec 2015 06:09:36 +0000 Subject: [PATCH 022/199] 8136410: AlgorithmDecomposer is not parsing padding correctly Reviewed-by: weijun --- .../security/util/AlgorithmDecomposer.java | 4 +- .../DecomposeAlgorithms.java | 71 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 jdk/test/sun/security/util/AlgorithmConstraints/DecomposeAlgorithms.java diff --git a/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java b/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java index 82e61cbb06b..4410a3102be 100644 --- a/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java +++ b/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java @@ -35,8 +35,10 @@ import java.util.regex.Pattern; public class AlgorithmDecomposer { private static final Pattern transPattern = Pattern.compile("/"); + + // '(? parsed = parser.decompose(fullAlgName); + if (parsed.size() != components.length) { + throw new Exception("Not expected components number: " + parsed); + } + + for (String component : components) { + if (!parsed.contains(component)) { + throw new Exception("Not a expected component: " + component); + } + } + + System.out.println("OK: " + fullAlgName); + } +} From edabf4afb36e8fd400f31742aac920a6b2528220 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Thu, 10 Dec 2015 10:04:41 +0000 Subject: [PATCH 023/199] 8145082: Remove sun.misc.Unsafe dependency from sun.nio.cs.StringUTF16 Reviewed-by: psandoz, sherman --- jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java index f1608cab5d5..13e1d8cca5a 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java @@ -25,8 +25,8 @@ package sun.nio.cs; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; -import static sun.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; +import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; +import static jdk.internal.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; class StringUTF16 { @@ -35,5 +35,5 @@ class StringUTF16 { ARRAY_BYTE_BASE_OFFSET + ARRAY_BYTE_INDEX_SCALE * index * 2L); } - private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + private static final jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); } From 6f930291df342af57807dfc210e264a104e229eb Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 10 Dec 2015 08:17:08 -0800 Subject: [PATCH 024/199] Added tag jdk-9+96 for changeset 25e9d31417fa --- jdk/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/.hgtags b/jdk/.hgtags index e1f07ad834d..e4b1c1f57c3 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -338,3 +338,4 @@ b433e4dfb830fea60e5187e4580791b62cc362d2 jdk9-b90 2f12392d0dde768150c83087cdbdd0d33a4d866c jdk9-b93 559b626b01179420a94feb9c3d0f246970d2e3fa jdk9-b94 8581faf0d474472e32f589bbc16db7eec912d83f jdk-9+95 +c021b855f51e572e63982654b17742cb1f814fb4 jdk-9+96 From 30abf404b38f170cbe180b49a7fe8502365bd38a Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 10 Dec 2015 15:57:27 -0800 Subject: [PATCH 025/199] 8139133: Changing the modification time on a unix domain socket file fails If a file descriptor cannot be obtained, use utimes() instead of futimes(). Reviewed-by: alanb --- jdk/make/mapfiles/libnio/mapfile-linux | 2 +- jdk/make/mapfiles/libnio/mapfile-macosx | 2 +- jdk/make/mapfiles/libnio/mapfile-solaris | 2 +- .../native/genconstants/fs/genUnixConstants.c | 3 +- .../sun/nio/fs/LinuxDosFileAttributeView.java | 8 +- .../classes/sun/nio/fs/LinuxFileStore.java | 28 ++-- .../fs/LinuxUserDefinedFileAttributeView.java | 37 ++++- .../nio/fs/SolarisAclFileAttributeView.java | 16 ++- .../SolarisUserDefinedFileAttributeView.java | 22 ++- .../classes/sun/nio/fs/UnixException.java | 5 +- .../sun/nio/fs/UnixFileAttributeViews.java | 26 +++- .../sun/nio/fs/UnixNativeDispatcher.java | 11 +- .../unix/classes/sun/nio/fs/UnixPath.java | 14 +- .../native/libnio/fs/UnixNativeDispatcher.c | 4 +- .../UnixSocketFile.java | 133 ++++++++++++++++++ 15 files changed, 256 insertions(+), 57 deletions(-) create mode 100644 jdk/test/java/nio/file/attribute/BasicFileAttributeView/UnixSocketFile.java diff --git a/jdk/make/mapfiles/libnio/mapfile-linux b/jdk/make/mapfiles/libnio/mapfile-linux index 428553e122c..b9b059a80c2 100644 --- a/jdk/make/mapfiles/libnio/mapfile-linux +++ b/jdk/make/mapfiles/libnio/mapfile-linux @@ -173,7 +173,7 @@ SUNWprivate_1.1 { Java_sun_nio_fs_UnixNativeDispatcher_futimes; Java_sun_nio_fs_UnixNativeDispatcher_open0; Java_sun_nio_fs_UnixNativeDispatcher_openat0; - Java_sun_nio_fs_UnixNativeDispatcher_close; + Java_sun_nio_fs_UnixNativeDispatcher_close0; Java_sun_nio_fs_UnixNativeDispatcher_read; Java_sun_nio_fs_UnixNativeDispatcher_write; Java_sun_nio_fs_UnixNativeDispatcher_fopen0; diff --git a/jdk/make/mapfiles/libnio/mapfile-macosx b/jdk/make/mapfiles/libnio/mapfile-macosx index 97207e2c816..6e4a7fb594c 100644 --- a/jdk/make/mapfiles/libnio/mapfile-macosx +++ b/jdk/make/mapfiles/libnio/mapfile-macosx @@ -150,7 +150,7 @@ SUNWprivate_1.1 { Java_sun_nio_fs_UnixNativeDispatcher_futimes; Java_sun_nio_fs_UnixNativeDispatcher_open0; Java_sun_nio_fs_UnixNativeDispatcher_openat0; - Java_sun_nio_fs_UnixNativeDispatcher_close; + Java_sun_nio_fs_UnixNativeDispatcher_close0; Java_sun_nio_fs_UnixNativeDispatcher_read; Java_sun_nio_fs_UnixNativeDispatcher_write; Java_sun_nio_fs_UnixNativeDispatcher_fopen0; diff --git a/jdk/make/mapfiles/libnio/mapfile-solaris b/jdk/make/mapfiles/libnio/mapfile-solaris index 12f418cad8a..6834bd221d4 100644 --- a/jdk/make/mapfiles/libnio/mapfile-solaris +++ b/jdk/make/mapfiles/libnio/mapfile-solaris @@ -150,7 +150,7 @@ SUNWprivate_1.1 { Java_sun_nio_fs_UnixNativeDispatcher_futimes; Java_sun_nio_fs_UnixNativeDispatcher_open0; Java_sun_nio_fs_UnixNativeDispatcher_openat0; - Java_sun_nio_fs_UnixNativeDispatcher_close; + Java_sun_nio_fs_UnixNativeDispatcher_close0; Java_sun_nio_fs_UnixNativeDispatcher_read; Java_sun_nio_fs_UnixNativeDispatcher_write; Java_sun_nio_fs_UnixNativeDispatcher_fopen0; diff --git a/jdk/make/src/native/genconstants/fs/genUnixConstants.c b/jdk/make/src/native/genconstants/fs/genUnixConstants.c index c2cfc0aba39..792ecf81415 100644 --- a/jdk/make/src/native/genconstants/fs/genUnixConstants.c +++ b/jdk/make/src/native/genconstants/fs/genUnixConstants.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -104,6 +104,7 @@ int main(int argc, const char* argv[]) { // errors DEF(ENOENT); + DEF(ENXIO); DEF(EACCES); DEF(EEXIST); DEF(ENOTDIR); diff --git a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxDosFileAttributeView.java b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxDosFileAttributeView.java index dba638e8da9..a60f2ad45f9 100644 --- a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxDosFileAttributeView.java +++ b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxDosFileAttributeView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -117,8 +117,9 @@ class LinuxDosFileAttributeView public DosFileAttributes readAttributes() throws IOException { file.checkRead(); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; try { + fd = file.openForAttributeAccess(followLinks); final UnixFileAttributes attrs = UnixFileAttributes.get(fd); final int dosAttribute = getDosAttribute(fd); @@ -253,8 +254,9 @@ class LinuxDosFileAttributeView private void updateDosAttribute(int flag, boolean enable) throws IOException { file.checkWrite(); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; try { + fd = file.openForAttributeAccess(followLinks); int oldValue = getDosAttribute(fd); int newValue = oldValue; if (enable) { diff --git a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java index 360a8d33ba7..217a8850d65 100644 --- a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java +++ b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -94,22 +94,20 @@ class LinuxFileStore // returns true if extended attributes enabled on file system where given // file resides, returns false if disabled or unable to determine. private boolean isExtendedAttributesEnabled(UnixPath path) { + int fd = -1; try { - int fd = path.openForAttributeAccess(false); - try { - // fgetxattr returns size if called with size==0 - byte[] name = Util.toBytes("user.java"); - LinuxNativeDispatcher.fgetxattr(fd, name, 0L, 0); + fd = path.openForAttributeAccess(false); + + // fgetxattr returns size if called with size==0 + byte[] name = Util.toBytes("user.java"); + LinuxNativeDispatcher.fgetxattr(fd, name, 0L, 0); + return true; + } catch (UnixException e) { + // attribute does not exist + if (e.errno() == UnixConstants.ENODATA) return true; - } catch (UnixException e) { - // attribute does not exist - if (e.errno() == UnixConstants.ENODATA) - return true; - } finally { - UnixNativeDispatcher.close(fd); - } - } catch (IOException ignore) { - // nothing we can do + } finally { + UnixNativeDispatcher.close(fd); } return false; } diff --git a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java index 18b880c550f..568341be3ec 100644 --- a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java +++ b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -97,7 +97,12 @@ class LinuxUserDefinedFileAttributeView if (System.getSecurityManager() != null) checkAccess(file.getPathForPermissionCheck(), true, false); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; + try { + fd = file.openForAttributeAccess(followLinks); + } catch (UnixException x) { + x.rethrowAsIOException(file); + } NativeBuffer buffer = null; try { int size = 1024; @@ -133,7 +138,12 @@ class LinuxUserDefinedFileAttributeView if (System.getSecurityManager() != null) checkAccess(file.getPathForPermissionCheck(), true, false); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; + try { + fd = file.openForAttributeAccess(followLinks); + } catch (UnixException x) { + x.rethrowAsIOException(file); + } try { // fgetxattr returns size if called with size==0 return fgetxattr(fd, nameAsBytes(file,name), 0L, 0); @@ -169,7 +179,12 @@ class LinuxUserDefinedFileAttributeView address = nb.address(); } - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; + try { + fd = file.openForAttributeAccess(followLinks); + } catch (UnixException x) { + x.rethrowAsIOException(file); + } try { try { int n = fgetxattr(fd, nameAsBytes(file,name), address, rem); @@ -236,7 +251,12 @@ class LinuxUserDefinedFileAttributeView } } - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; + try { + fd = file.openForAttributeAccess(followLinks); + } catch (UnixException x) { + x.rethrowAsIOException(file); + } try { try { fsetxattr(fd, nameAsBytes(file,name), address, rem); @@ -260,7 +280,12 @@ class LinuxUserDefinedFileAttributeView if (System.getSecurityManager() != null) checkAccess(file.getPathForPermissionCheck(), false, true); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; + try { + fd = file.openForAttributeAccess(followLinks); + } catch (UnixException x) { + x.rethrowAsIOException(file); + } try { fremovexattr(fd, nameAsBytes(file,name)); } catch (UnixException x) { diff --git a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java index 7027bd96c2a..004cb4e3bef 100644 --- a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java +++ b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -309,7 +309,12 @@ class SolarisAclFileAttributeView checkAccess(file, true, false); // open file (will fail if file is a link and not following links) - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; + try { + fd = file.openForAttributeAccess(followLinks); + } catch (UnixException x) { + x.rethrowAsIOException(file); + } try { long address = unsafe.allocateMemory(SIZEOF_ACE_T * MAX_ACL_ENTRIES); try { @@ -338,7 +343,12 @@ class SolarisAclFileAttributeView checkAccess(file, false, true); // open file (will fail if file is a link and not following links) - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; + try { + fd = file.openForAttributeAccess(followLinks); + } catch (UnixException x) { + x.rethrowAsIOException(file); + } try { // SECURITY: need to copy list as can change during processing acl = new ArrayList(acl); diff --git a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java index d5edf3f3962..baa92cddb96 100644 --- a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java +++ b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -71,9 +71,11 @@ class SolarisUserDefinedFileAttributeView if (System.getSecurityManager() != null) checkAccess(file.getPathForPermissionCheck(), true, false); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; try { try { + fd = file.openForAttributeAccess(followLinks); + // open extended attribute directory int dfd = openat(fd, HERE, (O_RDONLY|O_XATTR), 0); long dp; @@ -112,9 +114,11 @@ class SolarisUserDefinedFileAttributeView if (System.getSecurityManager() != null) checkAccess(file.getPathForPermissionCheck(), true, false); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; try { try { + fd = file.openForAttributeAccess(followLinks); + // open attribute file int afd = openat(fd, nameAsBytes(file,name), (O_RDONLY|O_XATTR), 0); try { @@ -142,9 +146,11 @@ class SolarisUserDefinedFileAttributeView if (System.getSecurityManager() != null) checkAccess(file.getPathForPermissionCheck(), true, false); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; try { try { + fd = file.openForAttributeAccess(followLinks); + // open attribute file int afd = openat(fd, nameAsBytes(file,name), (O_RDONLY|O_XATTR), 0); @@ -181,9 +187,11 @@ class SolarisUserDefinedFileAttributeView if (System.getSecurityManager() != null) checkAccess(file.getPathForPermissionCheck(), false, true); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; try { try { + fd = file.openForAttributeAccess(followLinks); + // open/create attribute file int afd = openat(fd, nameAsBytes(file,name), (O_CREAT|O_WRONLY|O_TRUNC|O_XATTR), @@ -217,8 +225,10 @@ class SolarisUserDefinedFileAttributeView if (System.getSecurityManager() != null) checkAccess(file.getPathForPermissionCheck(), false, true); - int fd = file.openForAttributeAccess(followLinks); + int fd = -1; try { + fd = file.openForAttributeAccess(followLinks); + int dfd = openat(fd, HERE, (O_RDONLY|O_XATTR), 0); try { unlinkat(dfd, nameAsBytes(file,name), 0); diff --git a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixException.java b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixException.java index 9481a75d891..22a41ebccc4 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixException.java +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -86,6 +86,9 @@ class UnixException extends Exception { return new NoSuchFileException(file, other, null); if (errno() == UnixConstants.EEXIST) return new FileAlreadyExistsException(file, other, null); + if (errno() == UnixConstants.ELOOP) + return new FileSystemException(file, other, errorString() + + " or unable to access attributes of symbolic link"); // fallback to the more general exception return new FileSystemException(file, other, errorString()); diff --git a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileAttributeViews.java b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileAttributeViews.java index 89d94aa02a8..6d43c0f4e95 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileAttributeViews.java +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileAttributeViews.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -71,14 +71,30 @@ class UnixFileAttributeViews { // permission check file.checkWrite(); - int fd = file.openForAttributeAccess(followLinks); + boolean haveFd = false; + boolean useFutimes = false; + int fd = -1; + try { + fd = file.openForAttributeAccess(followLinks); + if (fd != -1) { + haveFd = true; + useFutimes = futimesSupported(); + } + } catch (UnixException x) { + if (x.errno() != UnixConstants.ENXIO) { + x.rethrowAsIOException(file); + } + } + try { // assert followLinks || !UnixFileAttributes.get(fd).isSymbolicLink(); // if not changing both attributes then need existing attributes if (lastModifiedTime == null || lastAccessTime == null) { try { - UnixFileAttributes attrs = UnixFileAttributes.get(fd); + UnixFileAttributes attrs = haveFd ? + UnixFileAttributes.get(fd) : + UnixFileAttributes.get(file, followLinks); if (lastModifiedTime == null) lastModifiedTime = attrs.lastModifiedTime(); if (lastAccessTime == null) @@ -94,7 +110,7 @@ class UnixFileAttributeViews { boolean retry = false; try { - if (futimesSupported()) { + if (useFutimes) { futimes(fd, accessValue, modValue); } else { utimes(file, accessValue, modValue); @@ -113,7 +129,7 @@ class UnixFileAttributeViews { if (modValue < 0L) modValue = 0L; if (accessValue < 0L) accessValue= 0L; try { - if (futimesSupported()) { + if (useFutimes) { futimes(fd, accessValue, modValue); } else { utimes(file, accessValue, modValue); diff --git a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java index b84fa5c3861..c64374125cb 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -91,9 +91,14 @@ class UnixNativeDispatcher { throws UnixException; /** - * close(int filedes) + * close(int filedes). If fd is -1 this is a no-op. */ - static native void close(int fd); + static void close(int fd) { + if (fd != -1) { + close0(fd); + } + } + private static native void close0(int fd); /** * FILE* fopen(const char *filename, const char* mode); diff --git a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixPath.java b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixPath.java index b219764d3e7..8382c8cdfe8 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixPath.java +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -764,11 +764,12 @@ class UnixPath implements Path { // -- file operations -- // package-private - int openForAttributeAccess(boolean followLinks) throws IOException { + int openForAttributeAccess(boolean followLinks) throws UnixException { int flags = O_RDONLY; if (!followLinks) { if (O_NOFOLLOW == 0) - throw new IOException("NOFOLLOW_LINKS is not supported on this platform"); + throw new UnixException + ("NOFOLLOW_LINKS is not supported on this platform"); flags |= O_NOFOLLOW; } try { @@ -778,12 +779,7 @@ class UnixPath implements Path { if (getFileSystem().isSolaris() && x.errno() == EINVAL) x.setError(ELOOP); - if (x.errno() == ELOOP) - throw new FileSystemException(getPathForExceptionMessage(), null, - x.getMessage() + " or unable to access attributes of symbolic link"); - - x.rethrowAsIOException(this); - return -1; // keep compile happy + throw x; } } diff --git a/jdk/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/jdk/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 262986f9c05..7e6e5286473 100644 --- a/jdk/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/jdk/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -408,7 +408,7 @@ Java_sun_nio_fs_UnixNativeDispatcher_openat0(JNIEnv* env, jclass this, jint dfd, } JNIEXPORT void JNICALL -Java_sun_nio_fs_UnixNativeDispatcher_close(JNIEnv* env, jclass this, jint fd) { +Java_sun_nio_fs_UnixNativeDispatcher_close0(JNIEnv* env, jclass this, jint fd) { int err; /* TDB - need to decide if EIO and other errors should cause exception */ RESTARTABLE(close((int)fd), err); diff --git a/jdk/test/java/nio/file/attribute/BasicFileAttributeView/UnixSocketFile.java b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/UnixSocketFile.java new file mode 100644 index 00000000000..caa712ebb2a --- /dev/null +++ b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/UnixSocketFile.java @@ -0,0 +1,133 @@ +/* + * 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. + */ + +/* @test + * @bug 8139133 + * @summary Verify ability to set time attributes of socket files with no device + * @requires os.family == "linux" + */ + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; + +public class UnixSocketFile { + private static final String TEST_SUB_DIR = "UnixSocketFile"; + private static final String SOCKET_FILE_NAME = "mysocket"; + private static final String CMD_BASE = "nc -lU"; + + public static void main(String[] args) + throws InterruptedException, IOException { + + // Create a new sub-directory of the nominal test directory in which + // 'nc' will create the socket file. + String testSubDir = System.getProperty("test.dir", ".") + + File.separator + TEST_SUB_DIR; + Path socketTestDir = Paths.get(testSubDir); + Files.createDirectory(socketTestDir); + + // Set the path of the socket file. + String socketFilePath = testSubDir + File.separator + + SOCKET_FILE_NAME; + + // Create a process which executes the nc (netcat) utility to create + // a socket file at the indicated location. + Process proc; + FileSystem fs = FileSystems.getDefault(); + try (WatchService ws = fs.newWatchService()) { + // Watch the test sub-directory to receive notification when an + // entry, i.e., the socket file, is added to the sub-directory. + WatchKey wk = socketTestDir.register(ws, + StandardWatchEventKinds.ENTRY_CREATE); + + // Execute the 'nc' command. + proc = Runtime.getRuntime().exec(CMD_BASE + " " + socketFilePath); + + // Wait until the socket file is created. + WatchKey key = ws.take(); + if (key != wk) { + throw new RuntimeException("Unknown entry created - expected: " + + wk.watchable() + ", actual: " + key.watchable()); + } + wk.cancel(); + } + + // Verify that the socket file in fact exists. + Path socketPath = fs.getPath(socketFilePath); + if (!Files.exists(socketPath)) { + throw new RuntimeException("Socket file " + socketFilePath + + " was not created by \"nc\" command."); + } + + // Retrieve the most recent access and modification times of the + // socket file; print the values. + BasicFileAttributeView attributeView = Files.getFileAttributeView( + socketPath, BasicFileAttributeView.class); + BasicFileAttributes oldAttributes = attributeView.readAttributes(); + FileTime oldAccessTime = oldAttributes.lastAccessTime(); + FileTime oldModifiedTime = oldAttributes.lastModifiedTime(); + System.out.println("Old times: " + oldAccessTime + + " " + oldModifiedTime); + + // Calculate the time to which the access and modification times of the + // socket file will be changed. + FileTime newFileTime = + FileTime.fromMillis(oldAccessTime.toMillis() + 1066); + + try { + // Set the access and modification times of the socket file. + attributeView.setTimes(newFileTime, newFileTime, null); + + // Retrieve the updated access and modification times of the + // socket file; print the values. + FileTime newAccessTime = null; + FileTime newModifiedTime = null; + BasicFileAttributes newAttributes = attributeView.readAttributes(); + newAccessTime = newAttributes.lastAccessTime(); + newModifiedTime = newAttributes.lastModifiedTime(); + System.out.println("New times: " + newAccessTime + " " + + newModifiedTime); + + // Verify that the updated times have the expected values. + if ((newAccessTime != null && !newAccessTime.equals(newFileTime)) + || (newModifiedTime != null + && !newModifiedTime.equals(newFileTime))) { + throw new RuntimeException("Failed to set correct times."); + } + } finally { + // Destry the process running netcat and delete the socket file. + proc.destroy(); + Files.delete(socketPath); + } + } +} From e5a6f24f64858dc925aea2a2d10b0e2ec3156cb5 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 10 Dec 2015 17:47:26 -0800 Subject: [PATCH 026/199] 8032027: Add BigInteger square root methods Add sqrt() and sqrtAndReminder() using Newton iteration Reviewed-by: darcy, lowasser --- .../share/classes/java/math/BigInteger.java | 49 +++++- .../classes/java/math/MutableBigInteger.java | 92 ++++++++++- .../java/math/BigInteger/BigIntegerTest.java | 152 +++++++++++++++++- 3 files changed, 290 insertions(+), 3 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/math/BigInteger.java b/jdk/src/java.base/share/classes/java/math/BigInteger.java index 4471de71198..1c44659daf4 100644 --- a/jdk/src/java.base/share/classes/java/math/BigInteger.java +++ b/jdk/src/java.base/share/classes/java/math/BigInteger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -2409,6 +2409,53 @@ public class BigInteger extends Number implements Comparable { } } + /** + * Returns the integer square root of this BigInteger. The integer square + * root of the corresponding mathematical integer {@code n} is the largest + * mathematical integer {@code s} such that {@code s*s <= n}. It is equal + * to the value of {@code floor(sqrt(n))}, where {@code sqrt(n)} denotes the + * real square root of {@code n} treated as a real. Note that the integer + * square root will be less than the real square root if the latter is not + * representable as an integral value. + * + * @return the integer square root of {@code this} + * @throws ArithmeticException if {@code this} is negative. (The square + * root of a negative integer {@code val} is + * {@code (i * sqrt(-val))} where i is the + * imaginary unit and is equal to + * {@code sqrt(-1)}.) + * @since 1.9 + */ + public BigInteger sqrt() { + if (this.signum < 0) { + throw new ArithmeticException("Negative BigInteger"); + } + + return new MutableBigInteger(this.mag).sqrt().toBigInteger(); + } + + /** + * Returns an array of two BigIntegers containing the integer square root + * {@code s} of {@code this} and its remainder {@code this - s*s}, + * respectively. + * + * @return an array of two BigIntegers with the integer square root at + * offset 0 and the remainder at offset 1 + * @throws ArithmeticException if {@code this} is negative. (The square + * root of a negative integer {@code val} is + * {@code (i * sqrt(-val))} where i is the + * imaginary unit and is equal to + * {@code sqrt(-1)}.) + * @see #sqrt() + * @since 1.9 + */ + public BigInteger[] sqrtAndRemainder() { + BigInteger s = sqrt(); + BigInteger r = this.subtract(s.square()); + assert r.compareTo(BigInteger.ZERO) >= 0; + return new BigInteger[] {s, r}; + } + /** * Returns a BigInteger whose value is the greatest common divisor of * {@code abs(this)} and {@code abs(val)}. Returns 0 if diff --git a/jdk/src/java.base/share/classes/java/math/MutableBigInteger.java b/jdk/src/java.base/share/classes/java/math/MutableBigInteger.java index 46e79a1da60..65eca1e743d 100644 --- a/jdk/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/jdk/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -1866,6 +1866,96 @@ class MutableBigInteger { return (r << 32) | (q & LONG_MASK); } + /** + * Calculate the integer square root {@code floor(sqrt(this))} where + * {@code sqrt(.)} denotes the mathematical square root. The contents of + * {@code this} are not changed. The value of {@code this} is assumed + * to be non-negative. + * + * @implNote The implementation is based on the material in Henry S. Warren, + * Jr., Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 279-282. + * + * @throws ArithmeticException if the value returned by {@code bitLength()} + * overflows the range of {@code int}. + * @return the integer square root of {@code this} + * @since 1.9 + */ + MutableBigInteger sqrt() { + // Special cases. + if (this.isZero()) { + return new MutableBigInteger(0); + } else if (this.value.length == 1 + && (this.value[0] & LONG_MASK) < 4) { // result is unity + return ONE; + } + + if (bitLength() <= 63) { + // Initial estimate is the square root of the positive long value. + long v = new BigInteger(this.value, 1).longValueExact(); + long xk = (long)Math.floor(Math.sqrt(v)); + + // Refine the estimate. + do { + long xk1 = (xk + v/xk)/2; + + // Terminate when non-decreasing. + if (xk1 >= xk) { + return new MutableBigInteger(new int[] { + (int)(xk >>> 32), (int)(xk & LONG_MASK) + }); + } + + xk = xk1; + } while (true); + } else { + // Set up the initial estimate of the iteration. + + // Obtain the bitLength > 63. + int bitLength = (int) this.bitLength(); + if (bitLength != this.bitLength()) { + throw new ArithmeticException("bitLength() integer overflow"); + } + + // Determine an even valued right shift into positive long range. + int shift = bitLength - 63; + if (shift % 2 == 1) { + shift++; + } + + // Shift the value into positive long range. + MutableBigInteger xk = new MutableBigInteger(this); + xk.rightShift(shift); + xk.normalize(); + + // Use the square root of the shifted value as an approximation. + double d = new BigInteger(xk.value, 1).doubleValue(); + BigInteger bi = BigInteger.valueOf((long)Math.ceil(Math.sqrt(d))); + xk = new MutableBigInteger(bi.mag); + + // Shift the approximate square root back into the original range. + xk.leftShift(shift / 2); + + // Refine the estimate. + MutableBigInteger xk1 = new MutableBigInteger(); + do { + // xk1 = (xk + n/xk)/2 + this.divide(xk, xk1, false); + xk1.add(xk); + xk1.rightShift(1); + + // Terminate when non-decreasing. + if (xk1.compare(xk) >= 0) { + return xk; + } + + // xk = xk1 + xk.copyValue(xk1); + + xk1.reset(); + } while (true); + } + } + /** * Calculate GCD of this and b. This and b are changed by the computation. */ diff --git a/jdk/test/java/math/BigInteger/BigIntegerTest.java b/jdk/test/java/math/BigInteger/BigIntegerTest.java index 1c589fb201b..f8632809fb0 100644 --- a/jdk/test/java/math/BigInteger/BigIntegerTest.java +++ b/jdk/test/java/math/BigInteger/BigIntegerTest.java @@ -26,7 +26,7 @@ * @library /lib/testlibrary/ * @build jdk.testlibrary.* * @run main BigIntegerTest - * @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946 4026465 8074460 8078672 + * @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946 4026465 8074460 8078672 8032027 * @summary tests methods in BigInteger (use -Dseed=X to set PRNG seed) * @run main/timeout=400 BigIntegerTest * @author madbot @@ -38,8 +38,15 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.math.BigDecimal; import java.math.BigInteger; import java.util.Random; +import java.util.function.ToIntFunction; +import java.util.stream.Collectors; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; import jdk.testlibrary.RandomFactory; /** @@ -243,6 +250,146 @@ public class BigIntegerTest { report("square for " + order + " bits", failCount1); } + private static void printErr(String msg) { + System.err.println(msg); + } + + private static int checkResult(BigInteger expected, BigInteger actual, + String failureMessage) { + if (expected.compareTo(actual) != 0) { + printErr(failureMessage + " - expected: " + expected + + ", actual: " + actual); + return 1; + } + return 0; + } + + private static void squareRootSmall() { + int failCount = 0; + + // A negative value should cause an exception. + BigInteger n = BigInteger.ONE.negate(); + BigInteger s; + try { + s = n.sqrt(); + // If sqrt() does not throw an exception that is a failure. + failCount++; + printErr("sqrt() of negative number did not throw an exception"); + } catch (ArithmeticException expected) { + // A negative value should cause an exception and is not a failure. + } + + // A zero value should return BigInteger.ZERO. + failCount += checkResult(BigInteger.ZERO, BigInteger.ZERO.sqrt(), + "sqrt(0) != BigInteger.ZERO"); + + // 1 <= value < 4 should return BigInteger.ONE. + long[] smalls = new long[] {1, 2, 3}; + for (long small : smalls) { + failCount += checkResult(BigInteger.ONE, + BigInteger.valueOf(small).sqrt(), "sqrt("+small+") != 1"); + } + + report("squareRootSmall", failCount); + } + + public static void squareRoot() { + squareRootSmall(); + + ToIntFunction f = (n) -> { + int failCount = 0; + + // square root of n^2 -> n + BigInteger n2 = n.pow(2); + failCount += checkResult(n, n2.sqrt(), "sqrt() n^2 -> n"); + + // square root of n^2 + 1 -> n + BigInteger n2up = n2.add(BigInteger.ONE); + failCount += checkResult(n, n2up.sqrt(), "sqrt() n^2 + 1 -> n"); + + // square root of (n + 1)^2 - 1 -> n + BigInteger up = + n.add(BigInteger.ONE).pow(2).subtract(BigInteger.ONE); + failCount += checkResult(n, up.sqrt(), "sqrt() (n + 1)^2 - 1 -> n"); + + // sqrt(n)^2 <= n + BigInteger s = n.sqrt(); + if (s.multiply(s).compareTo(n) > 0) { + failCount++; + printErr("sqrt(n)^2 > n for n = " + n); + } + + // (sqrt(n) + 1)^2 > n + if (s.add(BigInteger.ONE).pow(2).compareTo(n) <= 0) { + failCount++; + printErr("(sqrt(n) + 1)^2 <= n for n = " + n); + } + + return failCount; + }; + + Stream.Builder sb = Stream.builder(); + int maxExponent = Double.MAX_EXPONENT + 1; + for (int i = 1; i <= maxExponent; i++) { + BigInteger p2 = BigInteger.ONE.shiftLeft(i); + sb.add(p2.subtract(BigInteger.ONE)); + sb.add(p2); + sb.add(p2.add(BigInteger.ONE)); + } + sb.add((new BigDecimal(Double.MAX_VALUE)).toBigInteger()); + sb.add((new BigDecimal(Double.MAX_VALUE)).toBigInteger().add(BigInteger.ONE)); + report("squareRoot for 2^N and 2^N - 1, 1 <= N <= Double.MAX_EXPONENT", + sb.build().collect(Collectors.summingInt(f))); + + IntStream ints = random.ints(SIZE, 4, Integer.MAX_VALUE); + report("squareRoot for int", ints.mapToObj(x -> + BigInteger.valueOf(x)).collect(Collectors.summingInt(f))); + + LongStream longs = random.longs(SIZE, (long)Integer.MAX_VALUE + 1L, + Long.MAX_VALUE); + report("squareRoot for long", longs.mapToObj(x -> + BigInteger.valueOf(x)).collect(Collectors.summingInt(f))); + + DoubleStream doubles = random.doubles(SIZE, + (double) Long.MAX_VALUE + 1.0, Math.sqrt(Double.MAX_VALUE)); + report("squareRoot for double", doubles.mapToObj(x -> + BigDecimal.valueOf(x).toBigInteger()).collect(Collectors.summingInt(f))); + } + + public static void squareRootAndRemainder() { + ToIntFunction g = (n) -> { + int failCount = 0; + BigInteger n2 = n.pow(2); + + // square root of n^2 -> n + BigInteger[] actual = n2.sqrtAndRemainder(); + failCount += checkResult(n, actual[0], "sqrtAndRemainder()[0]"); + failCount += checkResult(BigInteger.ZERO, actual[1], + "sqrtAndRemainder()[1]"); + + // square root of n^2 + 1 -> n + BigInteger n2up = n2.add(BigInteger.ONE); + actual = n2up.sqrtAndRemainder(); + failCount += checkResult(n, actual[0], "sqrtAndRemainder()[0]"); + failCount += checkResult(BigInteger.ONE, actual[1], + "sqrtAndRemainder()[1]"); + + // square root of (n + 1)^2 - 1 -> n + BigInteger up = + n.add(BigInteger.ONE).pow(2).subtract(BigInteger.ONE); + actual = up.sqrtAndRemainder(); + failCount += checkResult(n, actual[0], "sqrtAndRemainder()[0]"); + BigInteger r = up.subtract(n2); + failCount += checkResult(r, actual[1], "sqrtAndRemainder()[1]"); + + return failCount; + }; + + IntStream bits = random.ints(SIZE, 3, Short.MAX_VALUE); + report("sqrtAndRemainder", bits.mapToObj(x -> + BigInteger.valueOf(x)).collect(Collectors.summingInt(g))); + } + public static void arithmetic(int order) { int failCount = 0; @@ -1101,6 +1248,9 @@ public class BigIntegerTest { square(ORDER_KARATSUBA_SQUARE); square(ORDER_TOOM_COOK_SQUARE); + squareRoot(); + squareRootAndRemainder(); + bitCount(); bitLength(); bitOps(order1); From 4c7b25d5bf1c143afd7ebc1dcac7a049f875ccea Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Fri, 11 Dec 2015 11:46:45 +0100 Subject: [PATCH 027/199] 8145106: Still intermittent build error building jdk/src/demo/solaris/jni/Poller/Poller.c Reviewed-by: tbell, dholmes --- jdk/make/CompileDemos.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/make/CompileDemos.gmk b/jdk/make/CompileDemos.gmk index 423378389cc..90796dd0f1e 100644 --- a/jdk/make/CompileDemos.gmk +++ b/jdk/make/CompileDemos.gmk @@ -459,7 +459,7 @@ ifeq ($(OPENJDK_TARGET_OS), solaris) # We can only compile native code after java has been compiled (since we # depend on generated .h files) $(SUPPORT_OUTPUTDIR)/demos/native/jni/Poller/Poller.o: \ - $(BUILD_DEMO_JAVA_Poller_COMPILE_TARGET) + $(BUILD_DEMO_JAVA_Poller) # Copy to image $(SUPPORT_OUTPUTDIR)/demos/image/jni/Poller/README.txt: \ From e67e132b0226231b0ca68b4c3d83796411f15587 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Fri, 13 Nov 2015 14:44:05 +0100 Subject: [PATCH 028/199] 8142398: ManagementAgent.status diagnostic command only outputs the specifically set properties Reviewed-by: sla --- .../share/classes/sun/management/Agent.java | 86 +++++++++++++++---- .../jmxremote/startstop/JMXStatusTest.java | 7 +- 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/jdk/src/java.management/share/classes/sun/management/Agent.java b/jdk/src/java.management/share/classes/sun/management/Agent.java index 6a236fd8f3a..5582b428f87 100644 --- a/jdk/src/java.management/share/classes/sun/management/Agent.java +++ b/jdk/src/java.management/share/classes/sun/management/Agent.java @@ -37,9 +37,13 @@ import java.net.InetAddress; import java.net.MalformedURLException; import java.net.UnknownHostException; import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; import java.util.MissingResourceException; import java.util.Properties; import java.util.ResourceBundle; +import java.util.function.Function; +import java.util.function.Predicate; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXServiceURL; @@ -60,6 +64,30 @@ public class Agent { * Agent status collector strategy class */ private static abstract class StatusCollector { + protected static final Map DEFAULT_PROPS = new HashMap<>(); + + static { + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.PORT, + ConnectorBootstrap.DefaultValues.PORT); + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.USE_LOCAL_ONLY, + ConnectorBootstrap.DefaultValues.USE_LOCAL_ONLY); + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.USE_AUTHENTICATION, + ConnectorBootstrap.DefaultValues.USE_AUTHENTICATION); + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.USE_SSL, + ConnectorBootstrap.DefaultValues.USE_SSL); + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.USE_REGISTRY_SSL, + ConnectorBootstrap.DefaultValues.USE_REGISTRY_SSL); + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.SSL_NEED_CLIENT_AUTH, + ConnectorBootstrap.DefaultValues.SSL_NEED_CLIENT_AUTH); + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.CONFIG_FILE_NAME, + ConnectorBootstrap.DefaultValues.CONFIG_FILE_NAME); + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.PASSWORD_FILE_NAME, + ConnectorBootstrap.DefaultValues.PASSWORD_FILE_NAME); + DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.ACCESS_FILE_NAME, + ConnectorBootstrap.DefaultValues.ACCESS_FILE_NAME); + + } + final protected StringBuilder sb = new StringBuilder(); final public String collect() { Properties agentProps = VMSupport.getAgentProperties(); @@ -93,28 +121,49 @@ public class Agent { private void addConnection(boolean remote, JMXServiceURL u) { appendConnectionHeader(remote); addConnectionDetails(u); - if (remote) { - addConfigProperties(); - } + addConfigProperties(); appendConnectionFooter(remote); } private void addConfigProperties() { appendConfigPropsHeader(); - boolean[] first = new boolean[] {true}; - Properties props = configProps != null ? - configProps : getManagementProperties(); - props.entrySet().stream().forEach((e) -> { - String key = (String)e.getKey(); - if (key.startsWith("com.sun.management.")) { - addConfigProp(key, e.getValue(), first[0]); - first[0] = false; + Properties remoteProps = configProps != null ? + configProps : getManagementProperties(); + Map props = new HashMap<>(DEFAULT_PROPS); + + if (remoteProps == null) { + // local connector only + String loc_only = System.getProperty( + ConnectorBootstrap.PropertyNames.USE_LOCAL_ONLY + ); + + if (loc_only != null && + !ConnectorBootstrap.DefaultValues.USE_LOCAL_ONLY.equals(loc_only)) { + props.put( + ConnectorBootstrap.PropertyNames.USE_LOCAL_ONLY, + loc_only + ); } - }); + } else { + props.putAll(remoteProps); + } + + props.entrySet().stream() + .filter(preprocess(Map.Entry::getKey, StatusCollector::isManagementProp)) + .forEach(this::addConfigProp); + appendConfigPropsFooter(); } + private static boolean isManagementProp(Object pName) { + return pName != null && pName.toString().startsWith("com.sun.management."); + } + + private static Predicate preprocess(Function f, Predicate p) { + return (T t) -> p.test(f.apply(t)); + } + abstract protected void addAgentStatus(boolean enabled); abstract protected void appendConnectionsHeader(); abstract protected void appendConnectionsFooter(); @@ -123,7 +172,7 @@ public class Agent { abstract protected void appendConnectionFooter(boolean remote); abstract protected void appendConfigPropsHeader(); abstract protected void appendConfigPropsFooter(); - abstract protected void addConfigProp(String key, Object value, boolean first); + abstract protected void addConfigProp(Map.Entry prop); } /** @@ -159,11 +208,14 @@ public class Agent { } @Override - protected void addConfigProp(String key, Object value, boolean first) { - if (!first) { - sb.append('\n'); + protected void addConfigProp(Map.Entry prop) { + sb.append(" ").append(prop.getKey()).append(" = ") + .append(prop.getValue()); + Object defVal = DEFAULT_PROPS.get(prop.getKey()); + if (defVal != null && defVal.equals(prop.getValue())) { + sb.append(" [default]"); } - sb.append(" ").append(key).append(" = ").append(value); + sb.append("\n"); } @Override diff --git a/jdk/test/sun/management/jmxremote/startstop/JMXStatusTest.java b/jdk/test/sun/management/jmxremote/startstop/JMXStatusTest.java index 84943bd69d9..5de4986742b 100644 --- a/jdk/test/sun/management/jmxremote/startstop/JMXStatusTest.java +++ b/jdk/test/sun/management/jmxremote/startstop/JMXStatusTest.java @@ -33,7 +33,7 @@ import jdk.testlibrary.ProcessTools; /** * @test - * @bug 8023093 8138748 + * @bug 8023093 8138748 8142398 * @summary Performs a sanity test for the ManagementAgent.status diagnostic command. * Management agent may be disabled, started (only local connections) and started. * The test asserts that the expected text is being printed. @@ -56,7 +56,8 @@ abstract public class JMXStatusTest { "Connection Type\\s*\\:\\s*local\\n+" + "Protocol\\s*\\:\\s*[a-z]+\\n+" + "Host\\s*\\:\\s*.+\\n+" + - "URL\\s*\\:\\s*service\\:jmx\\:.+", + "URL\\s*\\:\\s*service\\:jmx\\:.+\\n+" + + "Properties\\s*\\:\\n+(\\s*\\S+\\s*=\\s*\\S+(\\s+\\[default\\])?\\n*)+", Pattern.MULTILINE ); @@ -67,7 +68,7 @@ abstract public class JMXStatusTest { "Protocol\\s*\\: [a-z]+\\n+" + "Host\\s*\\: .+\\n+" + "URL\\s*\\: service\\:jmx\\:.+\\n+" + - "Properties\\s*\\:\\n+(\\s*\\S+\\s*=\\s*\\S+\\n*)+", + "Properties\\s*\\:\\n+(\\s*\\S+\\s*=\\s*\\S+(\\s+\\[default\\])?\\n*)+", Pattern.MULTILINE | Pattern.DOTALL ); From d48c3fcafbf93177919b0e37785a340e0789789b Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Fri, 13 Nov 2015 18:36:14 +0400 Subject: [PATCH 029/199] 8137571: Linux HiDPI Graphics support Reviewed-by: flar, serb --- jdk/make/mapfiles/libawt/mapfile-vers-linux | 1 + jdk/make/mapfiles/libawt_xawt/mapfile-vers | 1 + .../unix/classes/sun/awt/X11/InfoWindow.java | 4 +- .../unix/classes/sun/awt/X11/XBaseWindow.java | 80 ++++++++++------ .../unix/classes/sun/awt/X11/XChoicePeer.java | 2 +- .../classes/sun/awt/X11/XComponentPeer.java | 10 +- .../classes/sun/awt/X11/XDecoratedPeer.java | 19 ++-- .../sun/awt/X11/XDragSourceContextPeer.java | 36 +++++--- .../sun/awt/X11/XEmbedClientHelper.java | 2 +- .../sun/awt/X11/XEmbeddedFramePeer.java | 28 +++--- .../classes/sun/awt/X11/XMenuBarPeer.java | 2 +- .../unix/classes/sun/awt/X11/XMenuWindow.java | 2 +- .../classes/sun/awt/X11/XMouseInfoPeer.java | 7 ++ .../classes/sun/awt/X11/XPopupMenuPeer.java | 2 +- .../unix/classes/sun/awt/X11/XRobotPeer.java | 10 +- .../unix/classes/sun/awt/X11/XToolkit.java | 65 ++++++++----- .../unix/classes/sun/awt/X11/XWM.java | 22 ++++- .../classes/sun/awt/X11/XWarningWindow.java | 8 +- .../unix/classes/sun/awt/X11/XWindow.java | 81 ++++++++++------ .../unix/classes/sun/awt/X11/XWindowPeer.java | 62 ++++++++----- .../unix/classes/sun/awt/X11/XlibUtil.java | 24 +++-- .../classes/sun/awt/X11GraphicsConfig.java | 26 +++++- .../classes/sun/awt/X11GraphicsDevice.java | 25 +++++ .../classes/sun/java2d/xr/XRSurfaceData.java | 42 +++++++-- .../sun/java2d/xr/XRSurfaceDataProxy.java | 5 +- .../java2d/xr/XRVolatileSurfaceManager.java | 8 +- .../native/libawt_xawt/awt/awt_GraphicsEnv.c | 35 +++++++ .../unix/native/libawt_xawt/awt/awt_Robot.c | 49 +++++++--- .../native/libawt_xawt/awt/gtk2_interface.c | 2 + .../native/libawt_xawt/awt/gtk2_interface.h | 9 ++ .../properties/HiDPIPropertiesLinuxTest.java | 92 +++++++++++++++++++ 31 files changed, 567 insertions(+), 194 deletions(-) create mode 100644 jdk/test/java/awt/hidpi/properties/HiDPIPropertiesLinuxTest.java diff --git a/jdk/make/mapfiles/libawt/mapfile-vers-linux b/jdk/make/mapfiles/libawt/mapfile-vers-linux index 81f5f037e51..5645cbd18d9 100644 --- a/jdk/make/mapfiles/libawt/mapfile-vers-linux +++ b/jdk/make/mapfiles/libawt/mapfile-vers-linux @@ -206,6 +206,7 @@ SUNWprivate_1.1 { Java_sun_awt_X11GraphicsDevice_enumDisplayModes; Java_sun_awt_X11GraphicsDevice_configDisplayMode; Java_sun_awt_X11GraphicsDevice_resetNativeData; + Java_sun_awt_X11GraphicsDevice_getNativeScaleFactor; Java_sun_awt_X11GraphicsEnvironment_checkShmExt; Java_sun_awt_X11GraphicsEnvironment_getDefaultScreenNum; Java_sun_awt_X11GraphicsEnvironment_getDisplayString; diff --git a/jdk/make/mapfiles/libawt_xawt/mapfile-vers b/jdk/make/mapfiles/libawt_xawt/mapfile-vers index 7e29cb3cb65..182ed0acfd7 100644 --- a/jdk/make/mapfiles/libawt_xawt/mapfile-vers +++ b/jdk/make/mapfiles/libawt_xawt/mapfile-vers @@ -214,6 +214,7 @@ SUNWprivate_1.1 { Java_sun_awt_X11GraphicsDevice_enumDisplayModes; Java_sun_awt_X11GraphicsDevice_configDisplayMode; Java_sun_awt_X11GraphicsDevice_resetNativeData; + Java_sun_awt_X11GraphicsDevice_getNativeScaleFactor; Java_sun_awt_X11GraphicsConfig_initIDs; Java_sun_awt_X11GraphicsConfig_getXResolution; Java_sun_awt_X11GraphicsConfig_getYResolution; diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java index 42fd317d69d..a7870a95b6a 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java @@ -80,9 +80,7 @@ public abstract class InfoWindow extends Window { pack(); Dimension size = getSize(); - // TODO: When 6356322 is fixed we should get screen bounds in - // this way: eframe.getGraphicsConfiguration().getBounds(). - Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize(); + Rectangle scrSize = getGraphicsConfiguration().getBounds(); if (corner.x < scrSize.width/2 && corner.y < scrSize.height/2) { // 1st square setLocation(corner.x + indent, corner.y + indent); diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java index ecca4d3081c..94cb9fdacaf 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java @@ -299,6 +299,22 @@ public class XBaseWindow { params.put(EVENT_MASK, Long.valueOf(eventMask)); } + /** + * Returns scale factor of the window. It is used to convert native + * coordinates to local and vice verse. + */ + protected int getScale() { + return 1; + } + + protected int scaleUp(int x) { + return x; + } + + protected int scaleDown(int x) { + return x; + } + /** * Creates window with parameters specified by params * @see #init @@ -366,15 +382,17 @@ public class XBaseWindow { log.fine("Creating window for " + this + " with the following attributes: \n" + params); } window = XlibWrapper.XCreateWindow(XToolkit.getDisplay(), - parentWindow.longValue(), - bounds.x, bounds.y, // location - bounds.width, bounds.height, // size - 0, // border - depth.intValue(), // depth - visual_class.intValue(), // class - visual.longValue(), // visual - value_mask, // value mask - xattr.pData); // attributes + parentWindow.longValue(), + scaleUp(bounds.x), + scaleUp(bounds.y), + scaleUp(bounds.width), + scaleUp(bounds.height), + 0, // border + depth.intValue(), // depth + visual_class.intValue(), // class + visual.longValue(), // visual + value_mask, // value mask + xattr.pData); // attributes if (window == 0) { throw new IllegalStateException("Couldn't create window because of wrong parameters. Run with NOISY_AWT to see details"); @@ -492,18 +510,18 @@ public class XBaseWindow { // we want to reset PPosition in hints. This is necessary // for locationByPlatform functionality if ((flags & XUtilConstants.PPosition) != 0) { - hints.set_x(x); - hints.set_y(y); + hints.set_x(scaleUp(x)); + hints.set_y(scaleUp(y)); } if ((flags & XUtilConstants.PSize) != 0) { - hints.set_width(width); - hints.set_height(height); + hints.set_width(scaleUp(width)); + hints.set_height(scaleUp(height)); } else if ((hints.get_flags() & XUtilConstants.PSize) != 0) { flags |= XUtilConstants.PSize; } if ((flags & XUtilConstants.PMinSize) != 0) { - hints.set_min_width(width); - hints.set_min_height(height); + hints.set_min_width(scaleUp(width)); + hints.set_min_height(scaleUp(height)); } else if ((hints.get_flags() & XUtilConstants.PMinSize) != 0) { flags |= XUtilConstants.PMinSize; //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced. @@ -512,31 +530,31 @@ public class XBaseWindow { if ((flags & XUtilConstants.PMaxSize) != 0) { if (maxBounds != null) { if (maxBounds.width != Integer.MAX_VALUE) { - hints.set_max_width(maxBounds.width); + hints.set_max_width(scaleUp(maxBounds.width)); } else { hints.set_max_width(XToolkit.getDefaultScreenWidth()); } if (maxBounds.height != Integer.MAX_VALUE) { - hints.set_max_height(maxBounds.height); + hints.set_max_height(scaleUp(maxBounds.height)); } else { hints.set_max_height(XToolkit.getDefaultScreenHeight()); } } else { - hints.set_max_width(width); - hints.set_max_height(height); + hints.set_max_width(scaleUp(width)); + hints.set_max_height(scaleUp(height)); } } else if ((hints.get_flags() & XUtilConstants.PMaxSize) != 0) { flags |= XUtilConstants.PMaxSize; if (maxBounds != null) { if (maxBounds.width != Integer.MAX_VALUE) { - hints.set_max_width(maxBounds.width); + hints.set_max_width(scaleUp(maxBounds.width)); } else { - hints.set_max_width(XToolkit.getDefaultScreenWidth()); + hints.set_max_width(scaleUp(XToolkit.getDefaultScreenWidth())); } if (maxBounds.height != Integer.MAX_VALUE) { - hints.set_max_height(maxBounds.height); + hints.set_max_height(scaleUp(maxBounds.height)); } else { - hints.set_max_height(XToolkit.getDefaultScreenHeight()); + hints.set_max_height(scaleUp(XToolkit.getDefaultScreenHeight())); } } else { // Leave intact @@ -723,7 +741,9 @@ public class XBaseWindow { height = Math.max(MIN_SIZE, height); XToolkit.awtLock(); try { - XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), getWindow(), x,y,width,height); + XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), getWindow(), + scaleUp(x), scaleUp(y), + scaleUp(width), scaleUp(height)); } finally { XToolkit.awtUnlock(); } @@ -756,7 +776,8 @@ public class XBaseWindow { rpt.x = x + srcPeer.getAbsoluteX(); rpt.y = y + srcPeer.getAbsoluteY(); } else { - rpt = XlibUtil.translateCoordinates(src, dst, new Point(x, y)); + int scale = srcPeer == null ? 1 : srcPeer.getScale(); + rpt = XlibUtil.translateCoordinates(src, dst, new Point(x, y), scale); } return rpt; } @@ -1042,10 +1063,11 @@ public class XBaseWindow { if (insLog.isLoggable(PlatformLogger.Level.FINER)) { insLog.finer("Configure, {0}", xe); } - x = xe.get_x(); - y = xe.get_y(); - width = xe.get_width(); - height = xe.get_height(); + + x = scaleDown(xe.get_x()); + y = scaleDown(xe.get_y()); + width = scaleDown(xe.get_width()); + height = scaleDown(xe.get_height()); } /** * Checks ButtonRelease released all Mouse buttons diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XChoicePeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XChoicePeer.java index b38297278c6..54155cf128e 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XChoicePeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XChoicePeer.java @@ -785,7 +785,7 @@ public class XChoicePeer extends XComponentPeer implements ChoicePeer, ToplevelS numItemsDisplayed = Math.min(MAX_UNFURLED_ITEMS, numItems); } Point global = XChoicePeer.this.toGlobal(0,0); - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + Rectangle screen = graphicsConfig.getBounds(); if (alignUnder != null) { Rectangle choiceRec = XChoicePeer.this.getBounds(); diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XComponentPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XComponentPeer.java index 5e8937436c0..e1331c7fdd0 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XComponentPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XComponentPeer.java @@ -158,7 +158,9 @@ public class XComponentPeer extends XWindow implements ComponentPeer, DropTarget XComponentPeer newPeer = (XComponentPeer)newNativeParent; XToolkit.awtLock(); try { - XlibWrapper.XReparentWindow(XToolkit.getDisplay(), getWindow(), newPeer.getContentWindow(), x, y); + XlibWrapper.XReparentWindow(XToolkit.getDisplay(), + getWindow(), newPeer.getContentWindow(), + scaleUp(x), scaleUp(y)); parentWindow = newPeer; } finally { XToolkit.awtUnlock(); @@ -1394,6 +1396,12 @@ public class XComponentPeer extends XWindow implements ComponentPeer, DropTarget XToolkit.awtLock(); try { if (shape != null) { + + int scale = getScale(); + if (scale != 1) { + shape = shape.getScaledRegion(scale, scale); + } + XlibWrapper.SetRectangularShape( XToolkit.getDisplay(), getWindow(), diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XDecoratedPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XDecoratedPeer.java index d5e2a0be3cd..74ee97f4f89 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XDecoratedPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XDecoratedPeer.java @@ -737,16 +737,12 @@ abstract class XDecoratedPeer extends XWindowPeer { updateChildrenSizes(); - // Bounds of the window - Rectangle targetBounds = AWTAccessor.getComponentAccessor().getBounds(target); - Point newLocation = getNewLocation(xe, currentInsets.left, currentInsets.top); - WindowDimensions newDimensions = new WindowDimensions(newLocation, - new Dimension(xe.get_width(), xe.get_height()), - copy(currentInsets), - true); + new Dimension(scaleDown(xe.get_width()), + scaleDown(xe.get_height())), + copy(currentInsets), true); if (insLog.isLoggable(PlatformLogger.Level.FINER)) { insLog.finer("Insets are {0}, new dimensions {1}", @@ -793,7 +789,8 @@ abstract class XDecoratedPeer extends XWindowPeer { try { updateSizeHints(rec.x, rec.y, rec.width, rec.height); XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), getShell(), - rec.x, rec.y, rec.width, rec.height); + scaleUp(rec.x), scaleUp(rec.y), + scaleUp(rec.width), scaleUp(rec.height)); } finally { XToolkit.awtUnlock(); @@ -806,7 +803,8 @@ abstract class XDecoratedPeer extends XWindowPeer { XToolkit.awtLock(); try { updateSizeHints(rec.x, rec.y, rec.width, rec.height); - XlibWrapper.XResizeWindow(XToolkit.getDisplay(), getShell(), rec.width, rec.height); + XlibWrapper.XResizeWindow(XToolkit.getDisplay(), getShell(), + scaleUp(rec.width), scaleUp(rec.height)); } finally { XToolkit.awtUnlock(); @@ -819,7 +817,8 @@ abstract class XDecoratedPeer extends XWindowPeer { XToolkit.awtLock(); try { updateSizeHints(rec.x, rec.y, rec.width, rec.height); - XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(), rec.x, rec.y); + XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(), + scaleUp(rec.x), scaleUp(rec.y)); } finally { XToolkit.awtUnlock(); diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XDragSourceContextPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XDragSourceContextPeer.java index 5b52c86b75d..5dcf06c59bf 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XDragSourceContextPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XDragSourceContextPeer.java @@ -83,6 +83,8 @@ public final class XDragSourceContextPeer private long[] sourceFormats = null; /* The XID of the root subwindow that contains the current target. */ private long targetRootSubwindow = 0; + /* window scale factor */ + int windowScale = 1; /* The pointer location. */ private int xRoot = 0; private int yRoot = 0; @@ -130,8 +132,8 @@ public final class XDragSourceContextPeer long xcursor = 0; long rootWindow = 0; - long dragWindow = 0; long timeStamp = 0; + windowScale = wpeer.getScale(); /* Retrieve the X cursor for the drag operation. */ { @@ -156,8 +158,6 @@ public final class XDragSourceContextPeer rootWindow = XlibWrapper.RootWindow(XToolkit.getDisplay(), screen); } - dragWindow = XWindow.getXAWTRootWindow().getWindow(); - timeStamp = XToolkit.getCurrentServerTime(); int dropActions = getDragSourceContext().getSourceActions(); @@ -441,8 +441,8 @@ public final class XDragSourceContextPeer private void updateTargetWindow(XMotionEvent xmotion) { assert XToolkit.isAWTLockHeldByCurrentThread(); - int x = xmotion.get_x_root(); - int y = xmotion.get_y_root(); + int x = scaleDown(xmotion.get_x_root()); + int y = scaleDown(xmotion.get_y_root()); long time = xmotion.get_time(); long subwindow = xmotion.get_subwindow(); @@ -498,9 +498,13 @@ public final class XDragSourceContextPeer if (!dragInProgress) { return; } - if (xRoot != xmotion.get_x_root() || yRoot != xmotion.get_y_root()) { - xRoot = xmotion.get_x_root(); - yRoot = xmotion.get_y_root(); + + int motionXRoot = scaleDown(xmotion.get_x_root()); + int motionYRoot = scaleDown(xmotion.get_y_root()); + + if (xRoot != motionXRoot || yRoot != motionYRoot) { + xRoot = motionXRoot; + yRoot = motionYRoot; postDragSourceDragEvent(targetAction, XWindow.getModifiers(xmotion.get_state(),0,0), @@ -519,8 +523,8 @@ public final class XDragSourceContextPeer updateTargetWindow(xmotion); if (dragProtocol != null) { - dragProtocol.sendMoveMessage(xmotion.get_x_root(), - xmotion.get_y_root(), + dragProtocol.sendMoveMessage(scaleDown(xmotion.get_x_root()), + scaleDown(xmotion.get_y_root()), sourceAction, sourceActions, xmotion.get_time()); } @@ -528,8 +532,8 @@ public final class XDragSourceContextPeer private void processDrop(XButtonEvent xbutton) { try { - dragProtocol.initiateDrop(xbutton.get_x_root(), - xbutton.get_y_root(), + dragProtocol.initiateDrop(scaleDown(xbutton.get_x_root()), + scaleDown(xbutton.get_y_root()), sourceAction, sourceActions, xbutton.get_time()); } catch (XException e) { @@ -805,4 +809,12 @@ public final class XDragSourceContextPeer dndInProgress = false; cleanup(XConstants.CurrentTime); } + + public int scaleUp(int x) { + return x * windowScale; + } + + public int scaleDown(int x) { + return x / windowScale; + } } diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XEmbedClientHelper.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XEmbedClientHelper.java index afbaaaaeb43..f8a8c8a5e03 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XEmbedClientHelper.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XEmbedClientHelper.java @@ -182,7 +182,7 @@ public class XEmbedClientHelper extends XEmbedHelper implements XEventDispatcher embedded.notifyStopped(); // check if newParent is a root window X11GraphicsConfig gc = (X11GraphicsConfig)embedded.getGraphicsConfiguration(); - X11GraphicsDevice gd = (X11GraphicsDevice)gc.getDevice(); + X11GraphicsDevice gd = gc.getDevice(); if ((newParent == XlibUtil.getRootWindow(gd.getScreen())) || (newParent == XToolkit.getDefaultRootWindow())) { diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XEmbeddedFramePeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XEmbeddedFramePeer.java index 72f0bc658b5..937c3a36cfc 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XEmbeddedFramePeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XEmbeddedFramePeer.java @@ -146,18 +146,18 @@ public class XEmbeddedFramePeer extends XFramePeer { // fix for 5063031 // if we use super.handleConfigureNotifyEvent() we would get wrong // size and position because embedded frame really is NOT a decorated one - checkIfOnNewScreen(toGlobal(new Rectangle(xe.get_x(), - xe.get_y(), - xe.get_width(), - xe.get_height()))); + checkIfOnNewScreen(toGlobal(new Rectangle(scaleDown(xe.get_x()), + scaleDown(xe.get_y()), + scaleDown(xe.get_width()), + scaleDown(xe.get_height())))); Rectangle oldBounds = getBounds(); synchronized (getStateLock()) { - x = xe.get_x(); - y = xe.get_y(); - width = xe.get_width(); - height = xe.get_height(); + x = scaleDown(xe.get_x()); + y = scaleDown(xe.get_y()); + width = scaleDown(xe.get_width()); + height = scaleDown(xe.get_height()); dimensions.setClientSize(width, height); dimensions.setLocation(x, y); @@ -215,10 +215,10 @@ public class XEmbeddedFramePeer extends XFramePeer { try { XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), getWindow(), attr.pData); - x = attr.get_x(); - y = attr.get_y(); - w = attr.get_width(); - h = attr.get_height(); + x = scaleDown(attr.get_x()); + y = scaleDown(attr.get_y()); + w = scaleDown(attr.get_width()); + h = scaleDown(attr.get_height()); } finally { XToolkit.awtUnlock(); } @@ -276,7 +276,7 @@ public class XEmbeddedFramePeer extends XFramePeer { { Point absoluteLoc = XlibUtil.translateCoordinates(getWindow(), XToolkit.getDefaultRootWindow(), - new Point(0, 0)); + new Point(0, 0), getScale()); return absoluteLoc != null ? absoluteLoc.x : 0; } @@ -284,7 +284,7 @@ public class XEmbeddedFramePeer extends XFramePeer { { Point absoluteLoc = XlibUtil.translateCoordinates(getWindow(), XToolkit.getDefaultRootWindow(), - new Point(0, 0)); + new Point(0, 0), getScale()); return absoluteLoc != null ? absoluteLoc.y : 0; } diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuBarPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuBarPeer.java index 2936b2af0c1..c9a8108a2e9 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuBarPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuBarPeer.java @@ -296,7 +296,7 @@ public class XMenuBarPeer extends XBaseMenuWindow implements MenuBarPeer { */ protected Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize) { Rectangle globalBounds = toGlobal(itemBounds); - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension screenSize = graphicsConfig.getBounds().getSize(); Rectangle res; res = fitWindowBelow(globalBounds, windowSize, screenSize); if (res != null) { diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java index 7bd63917663..76d034adf89 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java @@ -278,7 +278,7 @@ public class XMenuWindow extends XBaseMenuWindow { */ protected Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize) { Rectangle globalBounds = toGlobal(itemBounds); - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension screenSize = graphicsConfig.getBounds().getSize(); Rectangle res; res = fitWindowRight(globalBounds, windowSize, screenSize); if (res != null) { diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMouseInfoPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMouseInfoPeer.java index 1ff294fd9fd..09c9450a176 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMouseInfoPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XMouseInfoPeer.java @@ -30,6 +30,7 @@ import java.awt.Window; import java.awt.GraphicsEnvironment; import java.awt.GraphicsDevice; import java.awt.peer.MouseInfoPeer; +import sun.awt.X11GraphicsDevice; import sun.awt.AWTAccessor; @@ -64,6 +65,12 @@ public class XMouseInfoPeer implements MouseInfoPeer { if (pointerFound) { point.x = Native.getInt(XlibWrapper.larg3); point.y = Native.getInt(XlibWrapper.larg4); + GraphicsDevice device = gds[i]; + if (device instanceof X11GraphicsDevice) { + int scale = ((X11GraphicsDevice) device).getScaleFactor(); + point.x = XlibUtil.scaleDown(point.x, scale); + point.y = XlibUtil.scaleDown(point.y, scale); + } return i; } } diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java index c510836a2fa..b976a2d71b3 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java @@ -215,7 +215,7 @@ public class XPopupMenuPeer extends XMenuWindow implements PopupMenuPeer { */ protected Rectangle getWindowBounds(Point origin, Dimension windowSize) { Rectangle globalBounds = new Rectangle(origin.x, origin.y, 0, 0); - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension screenSize = graphicsConfig.getBounds().getSize(); Rectangle res; res = fitWindowRight(globalBounds, windowSize, screenSize); if (res != null) { diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java index 156abd9ce68..6d8473cb47c 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java @@ -64,7 +64,7 @@ class XRobotPeer implements RobotPeer { @Override public void mouseMove(int x, int y) { - mouseMoveImpl(xgc, x, y); + mouseMoveImpl(xgc, xgc.scaleUp(x), xgc.scaleUp(y)); } @Override @@ -95,7 +95,8 @@ class XRobotPeer implements RobotPeer { @Override public int getRGBPixel(int x, int y) { int pixelArray[] = new int[1]; - getRGBPixelsImpl(xgc, x, y, 1, 1, pixelArray, isGtkSupported); + getRGBPixelsImpl(xgc, x, y, 1, 1, xgc.getScale(), pixelArray, + isGtkSupported); return pixelArray[0]; } @@ -103,7 +104,7 @@ class XRobotPeer implements RobotPeer { public int [] getRGBPixels(Rectangle bounds) { int pixelArray[] = new int[bounds.width*bounds.height]; getRGBPixelsImpl(xgc, bounds.x, bounds.y, bounds.width, bounds.height, - pixelArray, isGtkSupported); + xgc.getScale(), pixelArray, isGtkSupported); return pixelArray; } @@ -118,5 +119,6 @@ class XRobotPeer implements RobotPeer { private static synchronized native void keyReleaseImpl(int keycode); private static synchronized native void getRGBPixelsImpl(X11GraphicsConfig xgc, - int x, int y, int width, int height, int pixelArray[], boolean isGtkSupported); + int x, int y, int width, int height, int scale, + int pixelArray[], boolean isGtkSupported); } diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java index 6b97e48a297..3852193bd4a 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java @@ -57,6 +57,7 @@ import sun.print.PrintJob2D; import sun.security.action.GetPropertyAction; import sun.security.action.GetBooleanAction; import sun.util.logging.PlatformLogger; +import static sun.awt.X11.XlibUtil.scaleDown; public final class XToolkit extends UNIXToolkit implements Runnable { private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XToolkit"); @@ -422,7 +423,7 @@ public final class XToolkit extends UNIXToolkit implements Runnable { } } - private void processGlobalMotionEvent(XEvent e) { + private void processGlobalMotionEvent(XEvent e, XBaseWindow win) { // Only our windows guaranteely generate MotionNotify, so we // should track enter/leave, to catch the moment when to // switch to XQueryPointer @@ -431,9 +432,11 @@ public final class XToolkit extends UNIXToolkit implements Runnable { awtLock(); try { if (lastCursorPos == null) { - lastCursorPos = new Point(ev.get_x_root(), ev.get_y_root()); + lastCursorPos = new Point(win.scaleDown(ev.get_x_root()), + win.scaleDown(ev.get_y_root())); } else { - lastCursorPos.setLocation(ev.get_x_root(), ev.get_y_root()); + lastCursorPos.setLocation(win.scaleDown(ev.get_x_root()), + win.scaleDown(ev.get_y_root())); } } finally { awtUnlock(); @@ -452,9 +455,11 @@ public final class XToolkit extends UNIXToolkit implements Runnable { awtLock(); try { if (lastCursorPos == null) { - lastCursorPos = new Point(ev.get_x_root(), ev.get_y_root()); + lastCursorPos = new Point(win.scaleDown(ev.get_x_root()), + win.scaleDown(ev.get_y_root())); } else { - lastCursorPos.setLocation(ev.get_x_root(), ev.get_y_root()); + lastCursorPos.setLocation(win.scaleDown(ev.get_x_root()), + win.scaleDown(ev.get_y_root())); } } finally { awtUnlock(); @@ -492,10 +497,11 @@ public final class XToolkit extends UNIXToolkit implements Runnable { private void dispatchEvent(XEvent ev) { final XAnyEvent xany = ev.get_xany(); - if (windowToXWindow(xany.get_window()) != null && - (ev.get_type() == XConstants.MotionNotify || ev.get_type() == XConstants.EnterNotify || ev.get_type() == XConstants.LeaveNotify)) - { - processGlobalMotionEvent(ev); + XBaseWindow baseWindow = windowToXWindow(xany.get_window()); + if (baseWindow != null && (ev.get_type() == XConstants.MotionNotify + || ev.get_type() == XConstants.EnterNotify + || ev.get_type() == XConstants.LeaveNotify)) { + processGlobalMotionEvent(ev, baseWindow); } if( ev.get_type() == XConstants.MappingNotify ) { @@ -670,8 +676,8 @@ public final class XToolkit extends UNIXToolkit implements Runnable { XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), pattr.pData); - screenWidth = pattr.get_width(); - screenHeight = pattr.get_height(); + screenWidth = config.scaleDown(pattr.get_width()); + screenHeight = config.scaleDown(pattr.get_height()); } finally { pattr.dispose(); } @@ -701,7 +707,7 @@ public final class XToolkit extends UNIXToolkit implements Runnable { return getDefaultScreenHeight(); } - private static Rectangle getWorkArea(long root) + private static Rectangle getWorkArea(long root, int scale) { XAtom XA_NET_WORKAREA = XAtom.get("_NET_WORKAREA"); @@ -717,7 +723,10 @@ public final class XToolkit extends UNIXToolkit implements Runnable { int rootWidth = (int)Native.getLong(native_ptr, 2); int rootHeight = (int)Native.getLong(native_ptr, 3); - return new Rectangle(rootX, rootY, rootWidth, rootHeight); + return new Rectangle(scaleDown(rootX, scale), + scaleDown(rootY, scale), + scaleDown(rootWidth, scale), + scaleDown(rootHeight, scale)); } } finally @@ -750,15 +759,16 @@ public final class XToolkit extends UNIXToolkit implements Runnable { try { X11GraphicsConfig x11gc = (X11GraphicsConfig)gc; - X11GraphicsDevice x11gd = (X11GraphicsDevice)x11gc.getDevice(); + X11GraphicsDevice x11gd = x11gc.getDevice(); long root = XlibUtil.getRootWindow(x11gd.getScreen()); - Rectangle rootBounds = XlibUtil.getWindowGeometry(root); + int scale = x11gc.getScale(); + Rectangle rootBounds = XlibUtil.getWindowGeometry(root, scale); X11GraphicsEnvironment x11ge = (X11GraphicsEnvironment) GraphicsEnvironment.getLocalGraphicsEnvironment(); if (!x11ge.runningXinerama()) { - Rectangle workArea = XToolkit.getWorkArea(root); + Rectangle workArea = XToolkit.getWorkArea(root, scale); if (workArea != null) { return new Insets(workArea.y, @@ -768,7 +778,7 @@ public final class XToolkit extends UNIXToolkit implements Runnable { } } - return getScreenInsetsManually(root, rootBounds, gc.getBounds()); + return getScreenInsetsManually(root, rootBounds, gc.getBounds(), scale); } finally { @@ -783,7 +793,8 @@ public final class XToolkit extends UNIXToolkit implements Runnable { * * This method should be called under XToolkit.awtLock() */ - private Insets getScreenInsetsManually(long root, Rectangle rootBounds, Rectangle screenBounds) + private Insets getScreenInsetsManually(long root, Rectangle rootBounds, + Rectangle screenBounds, int scale) { /* * During the manual calculation of screen insets we iterate @@ -831,20 +842,23 @@ public final class XToolkit extends UNIXToolkit implements Runnable { if (strutPresent) { // second, verify that window is located on the proper screen - Rectangle windowBounds = XlibUtil.getWindowGeometry(window); + Rectangle windowBounds = XlibUtil.getWindowGeometry(window, + scale); if (windowLevel > 1) { - windowBounds = XlibUtil.translateCoordinates(window, root, windowBounds); + windowBounds = XlibUtil.translateCoordinates(window, root, + windowBounds, + scale); } // if _NET_WM_STRUT_PARTIAL is present, we should use its values to detect // if the struts area intersects with screenBounds, however some window // managers don't set this hint correctly, so we just get intersection with windowBounds if (windowBounds != null && windowBounds.intersects(screenBounds)) { - int left = (int)Native.getLong(native_ptr, 0); - int right = (int)Native.getLong(native_ptr, 1); - int top = (int)Native.getLong(native_ptr, 2); - int bottom = (int)Native.getLong(native_ptr, 3); + int left = scaleDown((int)Native.getLong(native_ptr, 0), scale); + int right = scaleDown((int)Native.getLong(native_ptr, 1), scale); + int top = scaleDown((int)Native.getLong(native_ptr, 2), scale); + int bottom = scaleDown((int)Native.getLong(native_ptr, 3), scale); /* * struts could be relative to root window bounds, so @@ -2487,7 +2501,8 @@ public final class XToolkit extends UNIXToolkit implements Runnable { oops_updated = false; long event_number = getEventNumber(); // Generate OOPS ConfigureNotify event - XlibWrapper.XMoveWindow(getDisplay(), win.getWindow(), ++oops_position, 0); + XlibWrapper.XMoveWindow(getDisplay(), win.getWindow(), + win.scaleUp(++oops_position), 0); // Change win position each time to avoid system optimization if (oops_position > 50) { oops_position = 0; diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWM.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWM.java index 61d2cbc380d..464c0dbde71 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWM.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWM.java @@ -1024,8 +1024,12 @@ final class XWM shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top); window.updateSizeHints(window.getDimensions()); requestWMExtents(window.getWindow()); - XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(), - shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height); + XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), + window.getShell(), + window.scaleUp(shellBounds.x), + window.scaleUp(shellBounds.y), + window.scaleUp(shellBounds.width), + window.scaleUp(shellBounds.height)); /* REMINDER: will need to revisit when setExtendedStateBounds is added */ //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced. //We need to update frame's minimum size, not to reset it @@ -1058,8 +1062,12 @@ final class XWM window.updateSizeHints(newDimensions); requestWMExtents(window.getWindow()); XToolkit.XSync(); - XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(), - shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height); + XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), + window.getShell(), + window.scaleUp(shellBounds.x), + window.scaleUp(shellBounds.y), + window.scaleUp(shellBounds.width), + window.scaleUp(shellBounds.height)); } if (!justChangeSize) { /* update decorations */ setShellDecor(window); @@ -1701,6 +1709,12 @@ final class XWM pattr.dispose(); } } + + correctWM.top = win.scaleUp(correctWM.top); + correctWM.bottom = win.scaleUp(correctWM.bottom); + correctWM.left = win.scaleUp(correctWM.left); + correctWM.right = win.scaleUp(correctWM.right); + if (storedInsets.get(win.getClass()) == null) { storedInsets.put(win.getClass(), correctWM); } diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWarningWindow.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWarningWindow.java index e1fa63e2604..81f7e84839a 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWarningWindow.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWarningWindow.java @@ -258,10 +258,10 @@ class XWarningWindow extends XWindow { super.handleExposeEvent(xev); XExposeEvent xe = xev.get_xexpose(); - final int x = xe.get_x(); - final int y = xe.get_y(); - final int width = xe.get_width(); - final int height = xe.get_height(); + final int x = scaleDown(xe.get_x()); + final int y = scaleDown(xe.get_y()); + final int width = scaleDown(xe.get_width()); + final int height = scaleDown(xe.get_height()); SunToolkit.executeOnEventHandlerThread(target, new Runnable() { public void run() { diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindow.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindow.java index 42a03359f1f..b431c14d9d1 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindow.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindow.java @@ -548,10 +548,11 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { if (isEventDisabled(xev)) { return; } - int x = xe.get_x(); - int y = xe.get_y(); - int w = xe.get_width(); - int h = xe.get_height(); + + int x = scaleDown(xe.get_x()); + int y = scaleDown(xe.get_y()); + int w = scaleDown(xe.get_width()); + int h = scaleDown(xe.get_height()); Component target = getEventSource(); ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor(); @@ -676,10 +677,11 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { when = xbe.get_time(); long jWhen = XToolkit.nowMillisUTC_offset(when); - int x = xbe.get_x(); - int y = xbe.get_y(); + int x = scaleDown(xbe.get_x()); + int y = scaleDown(xbe.get_y()); if (xev.get_xany().get_window() != window) { - Point localXY = toLocal(xbe.get_x_root(), xbe.get_y_root()); + Point localXY = toLocal(scaleDown(xbe.get_x_root()), + scaleDown(xbe.get_y_root())); x = localXY.x; y = localXY.y; } @@ -730,8 +732,8 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { MouseEvent me = new MouseEvent(getEventSource(), type == XConstants.ButtonPress ? MouseEvent.MOUSE_PRESSED : MouseEvent.MOUSE_RELEASED, jWhen,modifiers, x, y, - xbe.get_x_root(), - xbe.get_y_root(), + scaleDown(xbe.get_x_root()), + scaleDown(xbe.get_y_root()), clickCount,popupTrigger,button); postEventToEventQueue(me); @@ -744,8 +746,8 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { jWhen, modifiers, x, y, - xbe.get_x_root(), - xbe.get_y_root(), + scaleDown(xbe.get_x_root()), + scaleDown(xbe.get_y_root()), clickCount, false, button)); } @@ -757,8 +759,8 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { MouseWheelEvent mwe = new MouseWheelEvent(getEventSource(),MouseEvent.MOUSE_WHEEL, jWhen, modifiers, x, y, - xbe.get_x_root(), - xbe.get_y_root(), + scaleDown(xbe.get_x_root()), + scaleDown(xbe.get_y_root()), 1,false,MouseWheelEvent.WHEEL_UNIT_SCROLL, 3,button==4 ? -1 : 1); postEventToEventQueue(mwe); @@ -805,8 +807,8 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { /* Fix for 6176814 . Add multiclick checking. */ - int x = xme.get_x(); - int y = xme.get_y(); + int x = scaleDown(xme.get_x()); + int y = scaleDown(xme.get_y()); XWindow lastWindow = (lastWindowRef != null) ? (lastWindowRef.get()):(null); if (!(lastWindow == this && @@ -828,7 +830,8 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { Component source = getEventSource(); if (xme.get_window() != window) { - Point localXY = toLocal(xme.get_x_root(), xme.get_y_root()); + Point localXY = toLocal(scaleDown(xme.get_x_root()), + scaleDown(xme.get_y_root())); x = localXY.x; y = localXY.y; } @@ -837,7 +840,9 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { */ if ((isDragging && clickCount == 0) || !isDragging) { MouseEvent mme = new MouseEvent(source, mouseEventType, jWhen, - modifiers, x, y, xme.get_x_root(), xme.get_y_root(), + modifiers, x, y, + scaleDown(xme.get_x_root()), + scaleDown(xme.get_y_root()), clickCount, popupTrigger, MouseEvent.NOBUTTON); postEventToEventQueue(mme); } @@ -949,10 +954,11 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { int modifiers = getModifiers(xce.get_state(),0,0); int clickCount = 0; boolean popupTrigger = false; - int x = xce.get_x(); - int y = xce.get_y(); + int x = scaleDown(xce.get_x()); + int y = scaleDown(xce.get_y()); if (xce.get_window() != window) { - Point localXY = toLocal(xce.get_x_root(), xce.get_y_root()); + Point localXY = toLocal(scaleDown(xce.get_x_root()), + scaleDown(xce.get_y_root())); x = localXY.x; y = localXY.y; } @@ -960,18 +966,27 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { // This code tracks boundary crossing and ensures MOUSE_ENTER/EXIT // are posted in alternate pairs if (compWithMouse != null) { - MouseEvent me = new MouseEvent(compWithMouse, - MouseEvent.MOUSE_EXITED, jWhen, modifiers, xce.get_x(), - xce.get_y(), xce.get_x_root(), xce.get_y_root(), clickCount, popupTrigger, - MouseEvent.NOBUTTON); + MouseEvent me = new MouseEvent(compWithMouse, MouseEvent.MOUSE_EXITED, + jWhen, modifiers, + scaleDown(xce.get_x()), + scaleDown(xce.get_y()), + scaleDown(xce.get_x_root()), + scaleDown(xce.get_y_root()), + clickCount, popupTrigger, + MouseEvent.NOBUTTON); postEventToEventQueue(me); eventLog.finest("Clearing last window ref"); lastWindowRef = null; } if (xce.get_type() == XConstants.EnterNotify) { MouseEvent me = new MouseEvent(getEventSource(), MouseEvent.MOUSE_ENTERED, - jWhen, modifiers, xce.get_x(), xce.get_y(), xce.get_x_root(), xce.get_y_root(), clickCount, - popupTrigger, MouseEvent.NOBUTTON); + jWhen, modifiers, + scaleDown(xce.get_x()), + scaleDown(xce.get_y()), + scaleDown(xce.get_x_root()), + scaleDown(xce.get_y_root()), + clickCount, popupTrigger, + MouseEvent.NOBUTTON); postEventToEventQueue(me); } } @@ -1531,4 +1546,18 @@ class XWindow extends XBaseWindow implements X11ComponentPeer { } } + @Override + protected int getScale() { + return graphicsConfig.getScale(); + } + + @Override + protected int scaleUp(int x) { + return graphicsConfig.scaleUp(x); + } + + @Override + protected int scaleDown(int x) { + return graphicsConfig.scaleDown(x); + } } diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java index fd6464b41d3..77fb29d3a97 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java @@ -29,6 +29,7 @@ import java.awt.*; import java.awt.event.ComponentEvent; import java.awt.event.FocusEvent; import java.awt.event.WindowEvent; +import java.awt.geom.AffineTransform; import java.awt.peer.ComponentPeer; import java.awt.peer.WindowPeer; @@ -750,10 +751,10 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, private Point queryXLocation() { - return XlibUtil.translateCoordinates( - getContentWindow(), - XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber()), - new Point(0, 0)); + return XlibUtil.translateCoordinates(getContentWindow(), XlibWrapper + .RootWindow(XToolkit.getDisplay(), + getScreenNumber()), + new Point(0, 0), getScale()); } protected Point getNewLocation(XConfigureEvent xe, int leftInset, int topInset) { @@ -764,7 +765,8 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, Point newLocation = targetBounds.getLocation(); if (xe.get_send_event() || runningWM == XWM.NO_WM || XWM.isNonReparentingWM()) { // Location, Client size + insets - newLocation = new Point(xe.get_x() - leftInset, xe.get_y() - topInset); + newLocation = new Point(scaleDown(xe.get_x()) - leftInset, + scaleDown(xe.get_y()) - topInset); } else { // ICCCM 4.1.5 states that a real ConfigureNotify will be sent when // a window is resized but the client can not tell if the window was @@ -807,12 +809,12 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, * See getNewLocation() for the details. */ Point newLocation = getNewLocation(xe, 0, 0); - xe.set_x(newLocation.x); - xe.set_y(newLocation.y); - checkIfOnNewScreen(new Rectangle(xe.get_x(), - xe.get_y(), - xe.get_width(), - xe.get_height())); + xe.set_x(scaleUp(newLocation.x)); + xe.set_y(scaleUp(newLocation.y)); + checkIfOnNewScreen(new Rectangle(newLocation.x, + newLocation.y, + scaleDown(xe.get_width()), + scaleDown(xe.get_height()))); // Don't call super until we've handled a screen change. Otherwise // there could be a race condition in which a ComponentListener could @@ -2115,7 +2117,9 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, XCrossingEvent xce = xev.get_xcrossing(); if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("{0}, when grabbed {1}, contains {2}", - xce, isGrabbed(), containsGlobal(xce.get_x_root(), xce.get_y_root())); + xce, isGrabbed(), + containsGlobal(scaleDown(xce.get_x_root()), + scaleDown(xce.get_y_root()))); } if (isGrabbed()) { // When window is grabbed, all events are dispatched to @@ -2141,7 +2145,9 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, XMotionEvent xme = xev.get_xmotion(); if (grabLog.isLoggable(PlatformLogger.Level.FINER)) { grabLog.finer("{0}, when grabbed {1}, contains {2}", - xme, isGrabbed(), containsGlobal(xme.get_x_root(), xme.get_y_root())); + xme, isGrabbed(), + containsGlobal(scaleDown(xme.get_x_root()), + scaleDown(xme.get_y_root()))); } if (isGrabbed()) { boolean dragging = false; @@ -2166,9 +2172,10 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, // So, I do not want to implement complicated logic for better retargeting. target = pressTarget.isVisible() ? pressTarget : this; xme.set_window(target.getWindow()); - Point localCoord = target.toLocal(xme.get_x_root(), xme.get_y_root()); - xme.set_x(localCoord.x); - xme.set_y(localCoord.y); + Point localCoord = target.toLocal(scaleDown(xme.get_x_root()), + scaleDown(xme.get_y_root())); + xme.set_x(scaleUp(localCoord.x)); + xme.set_y(scaleUp(localCoord.y)); } if (grabLog.isLoggable(PlatformLogger.Level.FINER)) { grabLog.finer(" - Grab event target {0}", target); @@ -2182,7 +2189,9 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, // note that we need to pass dragging events to the grabber (6390326) // see comment above for more inforamtion. - if (!containsGlobal(xme.get_x_root(), xme.get_y_root()) && !dragging) { + if (!containsGlobal(scaleDown(xme.get_x_root()), + scaleDown(xme.get_y_root())) + && !dragging) { // Outside of Java return; } @@ -2195,7 +2204,6 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, public void handleButtonPressRelease(XEvent xev) { XButtonEvent xbe = xev.get_xbutton(); - /* * Ignore the buttons above 20 due to the bit limit for * InputEvent.BUTTON_DOWN_MASK. @@ -2206,7 +2214,11 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, } if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("{0}, when grabbed {1}, contains {2} ({3}, {4}, {5}x{6})", - xbe, isGrabbed(), containsGlobal(xbe.get_x_root(), xbe.get_y_root()), getAbsoluteX(), getAbsoluteY(), getWidth(), getHeight()); + xbe, isGrabbed(), + containsGlobal(scaleDown(xbe.get_x_root()), + scaleDown(xbe.get_y_root())), + getAbsoluteX(), getAbsoluteY(), + getWidth(), getHeight()); } if (isGrabbed()) { // When window is grabbed, all events are dispatched to @@ -2232,9 +2244,10 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, // see 6390326 for more information. target = pressTarget.isVisible() ? pressTarget : this; xbe.set_window(target.getWindow()); - Point localCoord = target.toLocal(xbe.get_x_root(), xbe.get_y_root()); - xbe.set_x(localCoord.x); - xbe.set_y(localCoord.y); + Point localCoord = target.toLocal(scaleDown(xbe.get_x_root()), + scaleDown(xbe.get_y_root())); + xbe.set_x(scaleUp(localCoord.x)); + xbe.set_y(scaleUp(localCoord.y)); pressTarget = this; } if (target != null && target != getContentXWindow() && target != this) { @@ -2246,7 +2259,10 @@ class XWindowPeer extends XPanelPeer implements WindowPeer, // Target is either us or our content window - // check that event is inside. 'Us' in case of // shell will mean that this will also filter out press on title - if ((target == this || target == getContentXWindow()) && !containsGlobal(xbe.get_x_root(), xbe.get_y_root())) { + if ((target == this || target == getContentXWindow()) + && !containsGlobal(scaleDown(xbe.get_x_root()), + scaleDown(xbe.get_y_root()))) + { // Outside this toplevel hierarchy // According to the specification of UngrabEvent, post it // when press occurs outside of the window and not on its owned windows diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XlibUtil.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XlibUtil.java index f06af9856ae..27d6eef57d7 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XlibUtil.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XlibUtil.java @@ -102,7 +102,7 @@ public class XlibUtil /** * Returns the bounds of the given window, in absolute coordinates */ - static Rectangle getWindowGeometry(long window) + static Rectangle getWindowGeometry(long window, int scale) { XToolkit.awtLock(); try @@ -126,7 +126,9 @@ public class XlibUtil long width = Native.getUInt(XlibWrapper.larg4); long height = Native.getUInt(XlibWrapper.larg5); - return new Rectangle(x, y, (int)width, (int)height); + return new Rectangle(scaleDown(x, scale), scaleDown(y, scale), + scaleDown((int) width, scale), + scaleDown((int) height, scale)); } finally { @@ -138,7 +140,7 @@ public class XlibUtil * Translates the given point from one window to another. Returns * null if the translation is failed */ - static Point translateCoordinates(long src, long dst, Point p) + static Point translateCoordinates(long src, long dst, Point p, int scale) { Point translated = null; @@ -146,7 +148,7 @@ public class XlibUtil try { XTranslateCoordinates xtc = - new XTranslateCoordinates(src, dst, p.x, p.y); + new XTranslateCoordinates(src, dst, p.x * scale, p.y * scale); try { int status = xtc.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); @@ -154,7 +156,8 @@ public class XlibUtil ((XErrorHandlerUtil.saved_error == null) || (XErrorHandlerUtil.saved_error.get_error_code() == XConstants.Success))) { - translated = new Point(xtc.get_dest_x(), xtc.get_dest_y()); + translated = new Point(scaleDown(xtc.get_dest_x(), scale), + scaleDown(xtc.get_dest_y(), scale)); } } finally @@ -174,9 +177,12 @@ public class XlibUtil * Translates the given rectangle from one window to another. * Returns null if the translation is failed */ - static Rectangle translateCoordinates(long src, long dst, Rectangle r) + static Rectangle translateCoordinates(long src, long dst, Rectangle r, + int scale) { - Point translatedLoc = translateCoordinates(src, dst, r.getLocation()); + Point translatedLoc = translateCoordinates(src, dst, r.getLocation(), + scale); + if (translatedLoc == null) { return null; @@ -406,4 +412,8 @@ public class XlibUtil return 1 << (7 + button); } } + + static int scaleDown(int x, int scale) { + return x / scale; + } } diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java index 9ac71d82993..1b3c4fa2e23 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java @@ -133,7 +133,7 @@ public class X11GraphicsConfig extends GraphicsConfiguration /** * Return the graphics device associated with this configuration. */ - public GraphicsDevice getDevice() { + public X11GraphicsDevice getDevice() { return screen; } @@ -256,7 +256,20 @@ public class X11GraphicsConfig extends GraphicsConfiguration * For image buffers, this Transform will be the Identity transform. */ public AffineTransform getDefaultTransform() { - return new AffineTransform(); + double scale = getScale(); + return AffineTransform.getScaleInstance(scale, scale); + } + + public int getScale() { + return getDevice().getScaleFactor(); + } + + public int scaleUp(int x) { + return x * getScale(); + } + + public int scaleDown(int x) { + return x / getScale(); } /** @@ -308,7 +321,14 @@ public class X11GraphicsConfig extends GraphicsConfiguration } public Rectangle getBounds() { - return pGetBounds(screen.getScreen()); + Rectangle rect = pGetBounds(screen.getScreen()); + if (getScale() != 1) { + rect.x = scaleDown(rect.x); + rect.y = scaleDown(rect.y); + rect.width = scaleDown(rect.width); + rect.height = scaleDown(rect.height); + } + return rect; } private native Rectangle pGetBounds(int screenNum); diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java index 56b4252f761..9f35f172014 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java @@ -43,6 +43,7 @@ import sun.java2d.xr.XRGraphicsConfig; import sun.java2d.loops.SurfaceType; import sun.awt.util.ThreadGroupUtils; +import sun.java2d.SunGraphicsEnvironment; import sun.misc.ManagedLocalsThread; /** @@ -63,9 +64,11 @@ public final class X11GraphicsDevice extends GraphicsDevice private SunDisplayChanger topLevels = new SunDisplayChanger(); private DisplayMode origDisplayMode; private boolean shutdownHookRegistered; + private final int scale; public X11GraphicsDevice(int screennum) { this.screen = screennum; + this.scale = initScaleFactor(); } /* @@ -279,6 +282,7 @@ public final class X11GraphicsDevice extends GraphicsDevice int width, int height, int displayMode); private static native void resetNativeData(int screen); + private static native int getNativeScaleFactor(int screen); /** * Returns true only if: @@ -509,6 +513,27 @@ public final class X11GraphicsDevice extends GraphicsDevice topLevels.add(client); } + public int getScaleFactor() { + return scale; + } + + private int initScaleFactor() { + + if (SunGraphicsEnvironment.isUIScaleEnabled()) { + + double debugScale = SunGraphicsEnvironment.getDebugScale(); + + if (debugScale >= 1) { + return (int) debugScale; + } + + int nativeScale = getNativeScaleFactor(screen); + return nativeScale >= 1 ? nativeScale : 1; + } + + return 1; + } + /** * Remove a DisplayChangeListener from this X11GraphicsDevice. */ diff --git a/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java b/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java index dcfd6325b35..aabdda0db1e 100644 --- a/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java +++ b/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java @@ -244,7 +244,8 @@ public abstract class XRSurfaceData extends XSurfaceData { int width, int height, ColorModel cm, Image image, long drawable, - int transparency) { + int transparency, + boolean isTexture) { int depth; // If we have a 32 bit color model for the window it needs // alpha to support translucency of the window so we need @@ -267,7 +268,7 @@ public abstract class XRSurfaceData extends XSurfaceData { return new XRPixmapSurfaceData (gc, width, height, image, getSurfaceType(gc, transparency), cm, drawable, transparency, - XRUtils.getPictureFormatForTransparency(transparency), depth); + XRUtils.getPictureFormatForTransparency(transparency), depth, isTexture); } protected XRSurfaceData(X11ComponentPeer peer, XRGraphicsConfig gc, @@ -542,11 +543,16 @@ public abstract class XRSurfaceData extends XSurfaceData { } public static class XRWindowSurfaceData extends XRSurfaceData { + + protected final int scale; + public XRWindowSurfaceData(X11ComponentPeer peer, XRGraphicsConfig gc, SurfaceType sType) { super(peer, gc, sType, peer.getColorModel(), peer.getColorModel().getPixelSize(), Transparency.OPAQUE); + this.scale = gc.getScale(); + if (isXRDrawableValid()) { // If we have a 32 bit color model for the window it needs // alpha to support translucency of the window so we need @@ -571,6 +577,8 @@ public abstract class XRSurfaceData extends XSurfaceData { public Rectangle getBounds() { Rectangle r = peer.getBounds(); r.x = r.y = 0; + r.width *= scale; + r.height *= scale; return r; } @@ -596,6 +604,16 @@ public abstract class XRSurfaceData extends XSurfaceData { super.invalidate(); } + + @Override + public double getDefaultScaleX() { + return scale; + } + + @Override + public double getDefaultScaleY() { + return scale; + } } public static class XRInternalSurfaceData extends XRSurfaceData { @@ -627,18 +645,20 @@ public abstract class XRSurfaceData extends XSurfaceData { int width; int height; int transparency; + private final int scale; public XRPixmapSurfaceData(XRGraphicsConfig gc, int width, int height, Image image, SurfaceType sType, ColorModel cm, long drawable, int transparency, int pictFormat, - int depth) { + int depth, boolean isTexture) { super(null, gc, sType, cm, depth, transparency); - this.width = width; - this.height = height; + this.scale = isTexture ? 1 : gc.getDevice().getScaleFactor(); + this.width = width * scale; + this.height = height * scale; offscreenImage = image; this.transparency = transparency; - initSurface(depth, width, height, drawable, pictFormat); + initSurface(depth, this.width, this.height, drawable, pictFormat); initXRender(pictFormat); makePipes(); @@ -696,6 +716,16 @@ public abstract class XRSurfaceData extends XSurfaceData { public Object getDestination() { return offscreenImage; } + + @Override + public double getDefaultScaleX() { + return scale; + } + + @Override + public double getDefaultScaleY() { + return scale; + } } public long getGC() { diff --git a/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceDataProxy.java b/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceDataProxy.java index 091d26ee83b..ddc621a3f5e 100644 --- a/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceDataProxy.java +++ b/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceDataProxy.java @@ -59,8 +59,9 @@ public class XRSurfaceDataProxy extends SurfaceDataProxy implements Transparency public SurfaceData validateSurfaceData(SurfaceData srcData, SurfaceData cachedData, int w, int h) { if (cachedData == null) { - cachedData = XRSurfaceData.createData(xrgc, w, h, xrgc - .getColorModel(), null, 0, getTransparency()); + cachedData = XRSurfaceData.createData(xrgc, w, h, + xrgc.getColorModel(), null, 0, + getTransparency(), true); } return cachedData; } diff --git a/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRVolatileSurfaceManager.java b/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRVolatileSurfaceManager.java index 8c99f25b305..12a3034cf20 100644 --- a/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRVolatileSurfaceManager.java +++ b/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRVolatileSurfaceManager.java @@ -59,10 +59,10 @@ public class XRVolatileSurfaceManager extends VolatileSurfaceManager { drawable = ((Long)context).longValue(); } sData = XRSurfaceData.createData(gc, - vImg.getWidth(), - vImg.getHeight(), - cm, vImg, drawable, - vImg.getTransparency()); + vImg.getWidth(), + vImg.getHeight(), + cm, vImg, drawable, + vImg.getTransparency(), false); } catch (NullPointerException ex) { sData = null; } catch (OutOfMemoryError er) { diff --git a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c index a1c9fc27c44..6c9c2b49f01 100644 --- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c +++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c @@ -2082,3 +2082,38 @@ Java_sun_awt_X11GraphicsDevice_exitFullScreenExclusive /** * End DisplayMode/FullScreen support */ + +int getScale(const char *name) { + char *uiScale = getenv(name); + if (uiScale != NULL) { + double scale = strtod(uiScale, NULL); + if (errno == ERANGE || scale < 1) { + return -1; + } + return (int) scale; + } + return -1; +} + +/* + * Class: sun_awt_X11GraphicsDevice + * Method: getNativeScaleFactor + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_sun_awt_X11GraphicsDevice_getNativeScaleFactor + (JNIEnv *env, jobject this, jint screen) { + + // for debug purposes + static int scale = -2.0; + + if (scale == -2) { + scale = getScale("J2D_UISCALE"); + } + + if (scale >= 1) { + return scale; + } + + return getScale("GDK_SCALE"); +} diff --git a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c index eed0a4e40df..c9172a6e5f6 100644 --- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c +++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c @@ -210,6 +210,7 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, jint jy, jint jwidth, jint jheight, + jint scale, jintArray pixelArray, jboolean isGtkSupported) { XImage *image; @@ -231,13 +232,18 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, AWT_LOCK(); + jint sx = jx * scale; + jint sy = jy * scale; + jint swidth = jwidth * scale; + jint sheight = jheight * scale; + rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen); if (!XGetWindowAttributes(awt_display, rootWindow, &attr) - || jx + jwidth <= attr.x - || attr.x + attr.width <= jx - || jy + jheight <= attr.y - || attr.y + attr.height <= jy) { + || sx + swidth <= attr.x + || attr.x + attr.width <= sx + || sy + sheight <= attr.y + || attr.y + attr.height <= sy) { AWT_UNLOCK(); return; // Does not intersect with root window @@ -246,14 +252,14 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, gboolean gtk_failed = TRUE; jint _x, _y; - jint x = MAX(jx, attr.x); - jint y = MAX(jy, attr.y); - jint width = MIN(jx + jwidth, attr.x + attr.width) - x; - jint height = MIN(jy + jheight, attr.y + attr.height) - y; + jint x = MAX(sx, attr.x); + jint y = MAX(sy, attr.y); + jint width = MIN(sx + swidth, attr.x + attr.width) - x; + jint height = MIN(sy + sheight, attr.y + attr.height) - y; - int dx = attr.x > jx ? attr.x - jx : 0; - int dy = attr.y > jy ? attr.y - jy : 0; + int dx = attr.x > sx ? attr.x - sx : 0; + int dy = attr.y > sy ? attr.y - sy : 0; int index; @@ -264,6 +270,19 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, pixbuf = (*fp_gdk_pixbuf_get_from_drawable)(NULL, root, NULL, x, y, 0, 0, width, height); + if (pixbuf && scale != 1) { + GdkPixbuf *scaledPixbuf; + x /= scale; + y /= scale; + width /= scale; + height /= scale; + dx /= scale; + dy /= scale; + scaledPixbuf = (*fp_gdk_pixbuf_scale_simple)(pixbuf, width, height, + GDK_INTERP_BILINEAR); + (*fp_g_object_unref)(pixbuf); + pixbuf = scaledPixbuf; + } if (pixbuf) { int nchan = (*fp_gdk_pixbuf_get_n_channels)(pixbuf); @@ -312,7 +331,7 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, } if (gtk_failed) { - image = getWindowImage(awt_display, rootWindow, x, y, width, height); + image = getWindowImage(awt_display, rootWindow, sx, sy, swidth, sheight); ary = (*env)->GetPrimitiveArrayCritical(env, pixelArray, NULL); @@ -322,10 +341,16 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, return; } + dx /= scale; + dy /= scale; + width /= scale; + height /= scale; + /* convert to Java ARGB pixels */ for (_y = 0; _y < height; _y++) { for (_x = 0; _x < width; _x++) { - jint pixel = (jint) XGetPixel(image, _x, _y); /* Note ignore upper + jint pixel = (jint) XGetPixel(image, _x * scale, _y * scale); + /* Note ignore upper * 32-bits on 64-bit * OSes. */ diff --git a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c index 6ef926e749a..3fa9151fb40 100644 --- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c +++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c @@ -648,6 +648,8 @@ gboolean gtk2_load(JNIEnv *env) fp_gdk_pixmap_new = dl_symbol("gdk_pixmap_new"); fp_gdk_pixbuf_get_from_drawable = dl_symbol("gdk_pixbuf_get_from_drawable"); + fp_gdk_pixbuf_scale_simple = + dl_symbol("gdk_pixbuf_scale_simple"); fp_gdk_gc_new = dl_symbol("gdk_gc_new"); fp_gdk_rgb_gc_set_foreground = dl_symbol("gdk_rgb_gc_set_foreground"); diff --git a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h index 06cf8bd5261..10f32dc3e36 100644 --- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h +++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h @@ -270,6 +270,13 @@ typedef enum G_PARAM_PRIVATE = 1 << 5 } GParamFlags; +typedef enum { + GDK_INTERP_NEAREST, + GDK_INTERP_TILES, + GDK_INTERP_BILINEAR, + GDK_INTERP_HYPER +} GdkInterpType; + /* We define all structure pointers to be void* */ typedef void GError; typedef void GMainContext; @@ -787,6 +794,8 @@ GdkColorspace (*fp_gdk_pixbuf_get_colorspace)(const GdkPixbuf *pixbuf); GdkPixbuf *(*fp_gdk_pixbuf_get_from_drawable)(GdkPixbuf *dest, GdkDrawable *src, GdkColormap *cmap, int src_x, int src_y, int dest_x, int dest_y, int width, int height); +GdkPixbuf *(*fp_gdk_pixbuf_scale_simple)(GdkPixbuf *src, + int dest_width, int dest_heigh, GdkInterpType interp_type); void (*fp_gtk_widget_destroy)(GtkWidget *widget); diff --git a/jdk/test/java/awt/hidpi/properties/HiDPIPropertiesLinuxTest.java b/jdk/test/java/awt/hidpi/properties/HiDPIPropertiesLinuxTest.java new file mode 100644 index 00000000000..aba2dc1f87c --- /dev/null +++ b/jdk/test/java/awt/hidpi/properties/HiDPIPropertiesLinuxTest.java @@ -0,0 +1,92 @@ +/* + * 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.Dialog; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import javax.swing.UIManager; + +/* @test + * @bug 8137571 + * @summary Linux HiDPI Graphics support + * @author Alexander Scherbatiy + * @requires (os.family == "linux") + * @run main/othervm -Dsun.java2d.uiScale.enabled=false + * -Dsun.java2d.uiScale=2 + * HiDPIPropertiesLinuxTest UISCALE_DISABLED + * HiDPIPropertiesTest UISCALE_DISABLED + * @run main/othervm -Dsun.java2d.uiScale.enabled=true + * -Dsun.java2d.uiScale=3 + * HiDPIPropertiesLinuxTest UISCALE_3 + * @run main/othervm -Dsun.java2d.uiScale=4 + * HiDPIPropertiesLinuxTest UISCALE_4 + */ +public class HiDPIPropertiesLinuxTest { + + public static void main(String[] args) throws Exception { + + try { + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); + } catch (Exception e) { + return; + } + + String testCase = args[0]; + switch (testCase) { + case "UISCALE_DISABLED": + testScale(1.0, 1.0); + break; + case "UISCALE_3": + testScale(3.0, 3.0); + break; + case "UISCALE_4": + testScale(4.0, 4.0); + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + } + + private static void testScale(double scaleX, double scaleY) { + + Dialog dialog = new Dialog((Frame) null, true) { + + @Override + public void paint(Graphics g) { + super.paint(g); + AffineTransform tx = ((Graphics2D) g).getTransform(); + dispose(); + if (scaleX != tx.getScaleX() || scaleY != tx.getScaleY()) { + throw new RuntimeException(String.format("Wrong scale:" + + "[%f, %f] instead of [%f, %f].", + tx.getScaleX(), tx.getScaleY(), scaleX, scaleY)); + } + } + }; + dialog.setSize(200, 300); + dialog.setVisible(true); + } +} From 00b0a8c46bca160e720ce84e73b019f5bb1a6bbb Mon Sep 17 00:00:00 2001 From: Rajeev Chamyal Date: Fri, 13 Nov 2015 18:46:16 +0400 Subject: [PATCH 030/199] 8079253: Test javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java fails Reviewed-by: serb, alexsch --- .../swing/SwingUtilities/TestBadBreak/TestBadBreak.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jdk/test/javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java b/jdk/test/javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java index 59806a33a0a..a4d1e0fb781 100644 --- a/jdk/test/javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java +++ b/jdk/test/javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java @@ -36,10 +36,10 @@ import static java.awt.image.BufferedImage.TYPE_INT_ARGB; /** * @test - * @bug 8015085 + * @bug 8015085 8079253 * @summary Shortening via " ... " is broken for Strings containing a combining * diaeresis. - * @author Sergey Bylokhov + * @run main TestBadBreak */ public class TestBadBreak { @@ -79,6 +79,7 @@ public class TestBadBreak { g2d.dispose(); } }; + label.setOpaque(true); frame.getContentPane().add(label); frame.setBounds(200, 200, 200, 90); } From 2603ac8fdbca6376ed2a69b7aa7371da732e1ab0 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 16 Nov 2015 10:56:21 +0300 Subject: [PATCH 031/199] 8081491: The case print incomplete Reviewed-by: alexsch, rchamyal --- .../classes/javax/swing/TablePrintable.java | 29 ++- .../javax/swing/plaf/basic/BasicTableUI.java | 23 ++- .../print/PageFormat/ImageableAreaTest.java | 77 +++++++- .../javax/swing/JTable/JTableScrollTest.java | 182 ++++++++++++++++++ 4 files changed, 298 insertions(+), 13 deletions(-) create mode 100644 jdk/test/javax/swing/JTable/JTableScrollTest.java 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 6b9053f4355..b47f8696662 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 @@ class TablePrintable implements Printable { */ 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 @@ class TablePrintable implements Printable { // 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 @@ class TablePrintable implements Printable { 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 @@ class TablePrintable implements Printable { // 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 @@ class TablePrintable implements Printable { 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 ef0c45463dd..73fd9a51340 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 @@ public class BasicTableUI extends TableUI 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 @@ public class BasicTableUI extends TableUI 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 f9e7b4bbea8..7bf37415e6e 100644 --- a/jdk/test/java/awt/print/PageFormat/ImageableAreaTest.java +++ b/jdk/test/java/awt/print/PageFormat/ImageableAreaTest.java @@ -45,7 +45,7 @@ import javax.swing.table.TableModel; /** * @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 @@ public class ImageableAreaTest { @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 @@ public class ImageableAreaTest { "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 @@ public class ImageableAreaTest { 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 @@ public class ImageableAreaTest { } } + 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 00000000000..5e0461cc9f9 --- /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); + } + + +} From 694262c33baed0f71c527348d71074c46991a621 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Mon, 16 Nov 2015 10:18:18 +0100 Subject: [PATCH 032/199] 8042997: Make intrinsic some or all check index/range methods Objects.checkIndex() intrinsic Reviewed-by: psandoz, shade --- .../share/classes/java/util/Objects.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/util/Objects.java b/jdk/src/java.base/share/classes/java/util/Objects.java index 65089553b87..dbee22b2cef 100644 --- a/jdk/src/java.base/share/classes/java/util/Objects.java +++ b/jdk/src/java.base/share/classes/java/util/Objects.java @@ -27,6 +27,7 @@ package java.util; import java.util.function.BiFunction; import java.util.function.Supplier; +import jdk.internal.HotSpotIntrinsicCandidate; /** * This class consists of {@code static} utility methods for operating @@ -416,14 +417,14 @@ public final class Objects { * @throws IndexOutOfBoundsException if the {@code index} is out of bounds * and the exception mapping function is {@code null} * @since 9 - */ - /* - @HotSpotIntrinsicCandidate - This method will be made intrinsic in C2 to guide HotSpot to perform - unsigned comparisons of the index and length when it is known the length is - a non-negative value (such as that of an array length or from the upper - bound of a loop) + * + * @implNote + * This method is made intrinsic in optimizing compilers to guide + * them to perform unsigned comparisons of the index and length + * when it is known the length is a non-negative value (such as + * that of an array length or from the upper bound of a loop) */ + @HotSpotIntrinsicCandidate public static int checkIndex(int index, int length, BiFunction oobe) throws T, IndexOutOfBoundsException { From 7d7c9563972879de5cb6240bb6f1b8393dfe1a1d Mon Sep 17 00:00:00 2001 From: Rajeev Chamyal Date: Mon, 16 Nov 2015 15:03:17 +0400 Subject: [PATCH 033/199] 6288609: JInternalFrame.setDefaultCloseOperation() interferes with "close" behavior Reviewed-by: psadhukhan, alexsch --- .../classes/javax/swing/JInternalFrame.java | 5 - .../6288609/TestJInternalFrameDispose.java | 158 ++++++++++++++++++ 2 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 jdk/test/javax/swing/JInternalFrame/6288609/TestJInternalFrameDispose.java diff --git a/jdk/src/java.desktop/share/classes/javax/swing/JInternalFrame.java b/jdk/src/java.desktop/share/classes/javax/swing/JInternalFrame.java index 2297fdacdc0..6d82ffda5af 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/JInternalFrame.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/JInternalFrame.java @@ -1753,11 +1753,6 @@ public class JInternalFrame extends JComponent implements if (isVisible()) { setVisible(false); } - if (isSelected()) { - try { - setSelected(false); - } catch (PropertyVetoException pve) {} - } if (!isClosed) { firePropertyChange(IS_CLOSED_PROPERTY, Boolean.FALSE, Boolean.TRUE); isClosed = true; diff --git a/jdk/test/javax/swing/JInternalFrame/6288609/TestJInternalFrameDispose.java b/jdk/test/javax/swing/JInternalFrame/6288609/TestJInternalFrameDispose.java new file mode 100644 index 00000000000..8da7c1646c5 --- /dev/null +++ b/jdk/test/javax/swing/JInternalFrame/6288609/TestJInternalFrameDispose.java @@ -0,0 +1,158 @@ +/* + * 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. + */ + +/* + * @test + * @bug 6288609 + * @summary JInternalFrame.setDefaultCloseOperation() interferes with "close" + behavior + * @library ../../regtesthelpers + * @build Util + * @run main TestJInternalFrameDispose + */ +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import javax.swing.JFrame; +import javax.swing.JDesktopPane; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; + +public class TestJInternalFrameDispose { + + private static JDesktopPane desktopPane; + private static JFrame frame = new JFrame("Test Frame"); + private static int count = 0; + private static JMenu menu; + private static JMenuBar menuBar; + private static JMenuItem menuItem; + private static Robot robot; + private static JInternalFrame internalFrame; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + createUI(); + } + }); + + robot.waitForIdle(); + executeTest(); + dispose(); + } + + private static void createUI() { + + desktopPane = new JDesktopPane(); + frame.getContentPane().add(desktopPane); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + menuBar = new JMenuBar(); + frame.setJMenuBar(menuBar); + + menu = new JMenu("File"); + menuBar.add(menu); + + menuItem = new JMenuItem("New Child"); + menuItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JInternalFrame f = new JInternalFrame("Child " + + (++count), true, true, true, true); + f.setDefaultCloseOperation( + JInternalFrame.DO_NOTHING_ON_CLOSE); + f.addInternalFrameListener(new InternalFrameAdapter() { + @Override + public void internalFrameClosing( + InternalFrameEvent e) { + e.getInternalFrame().dispose(); + } + }); + f.setSize(200, 300); + f.setLocation(count * 20, count * 20); + desktopPane.add(f); + f.setVisible(true); + } + }); + menu.add(menuItem); + + frame.setSize(400, 500); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void executeTest() throws Exception { + + Point point = Util.getCenterPoint(menu); + performMouseOperations(point); + point = Util.getCenterPoint(menuItem); + performMouseOperations(point); + point = Util.getCenterPoint(menu); + performMouseOperations(point); + point = Util.getCenterPoint(menuItem); + performMouseOperations(point); + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + internalFrame = desktopPane.getSelectedFrame(); + internalFrame.doDefaultCloseAction(); + internalFrame = desktopPane.getSelectedFrame(); + } + }); + + robot.delay(2000); + if (internalFrame == null) { + dispose(); + throw new RuntimeException("Test Failed"); + } + } + + private static void dispose() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + frame.dispose(); + } + }); + } + + private static void performMouseOperations(Point point) { + robot.mouseMove(point.x, point.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.delay(1000); + robot.waitForIdle(); + } +} From 8b00726f061fbfc65ea0a02bef1e2b41f53e6707 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 16 Nov 2015 16:07:46 -0800 Subject: [PATCH 034/199] 7162125: [macosx] A font has different behaviour for ligatures depending on its creation mod Reviewed-by: srl, jgodinez --- .../sun/font/CCompositeGlyphMapper.java | 155 +++++++++++++++++ .../macosx/classes/sun/font/CFont.java | 60 ++++++- .../macosx/classes/sun/font/CStrike.java | 2 +- .../macosx/native/libawt_lwawt/font/AWTFont.h | 3 + .../macosx/native/libawt_lwawt/font/AWTFont.m | 159 ++++++++++++++++++ .../share/classes/sun/font/CompositeFont.java | 19 +++ .../sun/font/CompositeGlyphMapper.java | 2 +- .../share/classes/sun/font/Font2D.java | 9 +- .../classes/sun/font/FontSubstitution.java | 38 +++++ .../share/classes/sun/font/GlyphLayout.java | 3 + .../classes/sun/font/StandardGlyphVector.java | 10 +- .../classes/sun/font/SunLayoutEngine.java | 5 +- .../share/classes/sun/font/TrueTypeFont.java | 6 +- .../awt/font/TextLayout/OSXLigatureTest.java | 82 +++++++++ 14 files changed, 540 insertions(+), 13 deletions(-) create mode 100644 jdk/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java create mode 100644 jdk/src/java.desktop/share/classes/sun/font/FontSubstitution.java create mode 100644 jdk/test/java/awt/font/TextLayout/OSXLigatureTest.java diff --git a/jdk/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java b/jdk/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java new file mode 100644 index 00000000000..bf03ab9dfdf --- /dev/null +++ b/jdk/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java @@ -0,0 +1,155 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.font; + +public final class CCompositeGlyphMapper extends CompositeGlyphMapper { + + private CompositeFont font; + private CharToGlyphMapper slotMappers[]; + + public CCompositeGlyphMapper(CompositeFont compFont) { + super(compFont); + font = compFont; + slotMappers = new CharToGlyphMapper[font.numSlots]; + missingGlyph = 0; + } + + private CharToGlyphMapper getSlotMapper(int slot) { + CharToGlyphMapper mapper = slotMappers[slot]; + if (mapper == null) { + mapper = font.getSlotFont(slot).getMapper(); + slotMappers[slot] = mapper; + } + return mapper; + } + + public boolean canDisplay(char ch) { + int glyph = charToGlyph(ch); + return glyph != missingGlyph; + } + + private int convertToGlyph(int unicode) { + for (int slot = 0; slot < font.numSlots; slot++) { + CharToGlyphMapper mapper = getSlotMapper(slot); + int glyphCode = mapper.charToGlyph(unicode); + // The CFont Mappers will return a negative code + // for fonts that will fill the glyph from fallbacks + // - cascading font in OSX-speak. But we need to be + // know here that only the codes > 0 are really present. + if (glyphCode > 0) { + glyphCode = compositeGlyphCode(slot, glyphCode); + return glyphCode; + } + } + return missingGlyph; + } + + public int getNumGlyphs() { + int numGlyphs = 0; + for (int slot=0; slot<1 /*font.numSlots*/; slot++) { + CharToGlyphMapper mapper = slotMappers[slot]; + if (mapper == null) { + mapper = font.getSlotFont(slot).getMapper(); + slotMappers[slot] = mapper; + } + numGlyphs += mapper.getNumGlyphs(); + } + return numGlyphs; + } + + public int charToGlyph(int unicode) { + return convertToGlyph(unicode); + } + + public int charToGlyph(char unicode) { + return convertToGlyph(unicode); + } + + public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { + + for (int i=0; i= HI_SURROGATE_START && + code <= HI_SURROGATE_END && i < count - 1) { + char low = unicodes[i + 1]; + + if (low >= LO_SURROGATE_START && + low <= LO_SURROGATE_END) { + code = (code - HI_SURROGATE_START) * + 0x400 + low - LO_SURROGATE_START + 0x10000; + glyphs[i + 1] = INVISIBLE_GLYPH_ID; + } + } + + glyphs[i] = convertToGlyph(code); + + if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { + continue; + } + else if (FontUtilities.isComplexCharCode(code)) { + return true; + } + else if (code >= 0x10000) { + i += 1; // Empty glyph slot after surrogate + continue; + } + } + + return false; + } + + public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { + for (int i=0; i= HI_SURROGATE_START && + code <= HI_SURROGATE_END && i < count - 1) { + char low = unicodes[i + 1]; + + if (low >= LO_SURROGATE_START && + low <= LO_SURROGATE_END) { + code = (code - HI_SURROGATE_START) * + 0x400 + low - LO_SURROGATE_START + 0x10000; + + glyphs[i] = convertToGlyph(code); + i += 1; // Empty glyph slot after surrogate + glyphs[i] = INVISIBLE_GLYPH_ID; + continue; + } + } + + glyphs[i] = convertToGlyph(code); + } + } + + public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { + for (int i=0; i listOfString); + + private CompositeFont createCompositeFont() { + ArrayList listOfString = new ArrayList(); + getCascadeList(nativeFontPtr, listOfString); + + FontManager fm = FontManagerFactory.getInstance(); + int numFonts = 1 + listOfString.size(); + PhysicalFont[] fonts = new PhysicalFont[numFonts]; + fonts[0] = this; + int idx = 1; + for (String s : listOfString) { + if (s.equals(".AppleSymbolsFB")) { + // Don't know why we get the weird name above .. replace. + s = "AppleSymbols"; + } + Font2D f2d = fm.findFont2D(s, Font.PLAIN, FontManager.NO_FALLBACK); + if (f2d == null || f2d == this) { + continue; + } + fonts[idx++] = (PhysicalFont)f2d; + } + if (idx < fonts.length) { + PhysicalFont[] orig = fonts; + fonts = new PhysicalFont[idx]; + System.arraycopy(orig, 0, fonts, 0, idx); + } + CompositeFont compFont = new CompositeFont(fonts); + compFont.mapper = new CCompositeGlyphMapper(compFont); + return compFont; + } + + private CompositeFont compFont; + + public CompositeFont getCompositeFont2D() { + if (compFont == null) { + compFont = createCompositeFont(); + } + return compFont; + } + protected synchronized void finalize() { if (nativeFontPtr != 0) { disposeNativeFont(nativeFontPtr); diff --git a/jdk/src/java.desktop/macosx/classes/sun/font/CStrike.java b/jdk/src/java.desktop/macosx/classes/sun/font/CStrike.java index 5296f5c87f0..c50533a5947 100644 --- a/jdk/src/java.desktop/macosx/classes/sun/font/CStrike.java +++ b/jdk/src/java.desktop/macosx/classes/sun/font/CStrike.java @@ -31,7 +31,7 @@ import java.util.*; import sun.awt.SunHints; -public final class CStrike extends FontStrike { +public final class CStrike extends PhysicalStrike { // Creates the native strike private static native long createNativeStrikePtr(long nativeFontPtr, diff --git a/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h b/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h index 026809952dc..a86f5c3859d 100644 --- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h +++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h @@ -26,6 +26,8 @@ #import #import +#import "fontscalerdefs.h" + #define MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE 256 @interface AWTFont : NSObject { @@ -33,6 +35,7 @@ NSFont *fFont; CGFontRef fNativeCGFont; BOOL fIsFakeItalic; + TTLayoutTableCache* layoutTableCache; } + (AWTFont *) awtFontForName:(NSString *)name diff --git a/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m b/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m index a82afc26888..548fd5015d6 100644 --- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m +++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m @@ -42,10 +42,33 @@ if (self) { fFont = [font retain]; fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL); + layoutTableCache = NULL; } return self; } +static TTLayoutTableCache* newCFontLayoutTableCache() { + TTLayoutTableCache* ltc = calloc(1, sizeof(TTLayoutTableCache)); + if (ltc) { + int i; + for(i=0;ientries[i].len = -1; + } + } + return ltc; +} + +static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) { + if (ltc) { + int i; + for(i=0;ientries[i].ptr) free (ltc->entries[i].ptr); + } + if (ltc->kernPairs) free(ltc->kernPairs); + free(ltc); + } +} + - (void) dealloc { [fFont release]; fFont = nil; @@ -53,6 +76,10 @@ if (fNativeCGFont) { CGFontRelease(fNativeCGFont); fNativeCGFont = NULL; + if (layoutTableCache != NULL) { + freeCFontLayoutTableCache(layoutTableCache); + layoutTableCache = NULL; + } } [super dealloc]; @@ -63,6 +90,10 @@ CGFontRelease(fNativeCGFont); fNativeCGFont = NULL; } + if (layoutTableCache != NULL) { + freeCFontLayoutTableCache(layoutTableCache); + layoutTableCache = NULL; + } [super finalize]; } @@ -343,6 +374,95 @@ JNF_COCOA_EXIT(env); #pragma mark --- sun.font.CFont JNI --- +/* + * Class: sun_font_CFont + * Method: getPlatformFontPtrNative + * Signature: (JI)[B + */ +JNIEXPORT jlong JNICALL +Java_sun_font_CFont_getCGFontPtrNative + (JNIEnv *env, jclass clazz, + jlong awtFontPtr) +{ + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + return (jlong)(awtFont->fNativeCGFont); +} + +/* + * Class: sun_font_CFont + * Method: getLayoutTableCacheNative + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL +Java_sun_font_CFont_getLayoutTableCacheNative + (JNIEnv *env, jclass clazz, + jlong awtFontPtr) +{ + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + if (awtFont->layoutTableCache == NULL) { + awtFont->layoutTableCache = newCFontLayoutTableCache(); + } + return (jlong)(awtFont->layoutTableCache); +} + +/* + * Class: sun_font_CFont + * Method: getTableBytesNative + * Signature: (JI)[B + */ +JNIEXPORT jbyteArray JNICALL +Java_sun_font_CFont_getTableBytesNative + (JNIEnv *env, jclass clazz, + jlong awtFontPtr, jint jtag) +{ + jbyteArray jbytes = NULL; +JNF_COCOA_ENTER(env); + + CTFontTableTag tag = (CTFontTableTag)jtag; + int i, found = 0; + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + NSFont* nsFont = awtFont->fFont; + CTFontRef ctfont = (CTFontRef)nsFont; + CFArrayRef tagsArray = + CTFontCopyAvailableTables(ctfont, kCTFontTableOptionNoOptions); + CFIndex numTags = CFArrayGetCount(tagsArray); + for (i=0; iNewByteArray(env, (jsize)tableLength); + if (jbytes == NULL) { + return NULL; + } + (*env)->SetByteArrayRegion(env, jbytes, 0, + (jsize)tableLength, + (jbyte*)tableBytes); + CFRelease(table); + +JNF_COCOA_EXIT(env); + + return jbytes; +} + /* * Class: sun_font_CFont * Method: initNativeFont @@ -460,3 +580,42 @@ Java_sun_awt_FontDescriptor_initIDs { } #endif + +/* + * Class: sun_awt_FontDescriptor + * Method: initIDs + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_sun_font_CFont_getCascadeList + (JNIEnv *env, jclass cls, jlong awtFontPtr, jobject arrayListOfString) +{ + jclass alc = (*env)->FindClass(env, "java/util/ArrayList"); + if (alc == NULL) return; + jmethodID addMID = (*env)->GetMethodID(env, alc, "add", "(Ljava/lang/Object;)Z"); + if (addMID == NULL) return; + + CFIndex i; + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + NSFont* nsFont = awtFont->fFont; + CTFontRef font = (CTFontRef)nsFont; + CFStringRef base = CTFontCopyFullName(font); + CFArrayRef codes = CFLocaleCopyISOLanguageCodes(); + +#ifdef DEBUG + NSLog(@"BaseFont is : %@", (NSString*)base); +#endif + CFArrayRef fds = CTFontCopyDefaultCascadeListForLanguages(font, codes); + CFIndex cnt = CFArrayGetCount(fds); + for (i=0; iCallBooleanMethod(env, arrayListOfString, addMID, jFontName); + (*env)->DeleteLocalRef(env, jFontName); + } +} diff --git a/jdk/src/java.desktop/share/classes/sun/font/CompositeFont.java b/jdk/src/java.desktop/share/classes/sun/font/CompositeFont.java index 7b19bb362cb..177570b9638 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/CompositeFont.java +++ b/jdk/src/java.desktop/share/classes/sun/font/CompositeFont.java @@ -149,6 +149,25 @@ public final class CompositeFont extends Font2D { } } + /* + * Build a composite from a set of individual slot fonts. + */ + CompositeFont(PhysicalFont[] slotFonts) { + + isStdComposite = false; + handle = new Font2DHandle(this); + fullName = slotFonts[0].fullName; + familyName = slotFonts[0].familyName; + style = slotFonts[0].style; + + numMetricsSlots = 1; /* Only the physical Font */ + numSlots = slotFonts.length; + + components = new PhysicalFont[numSlots]; + System.arraycopy(slotFonts, 0, components, 0, numSlots); + deferredInitialisation = new boolean[numSlots]; // all false. + } + /* This method is currently intended to be called only from * FontManager.getCompositeFontUIResource(Font) * It creates a new CompositeFont with the contents of the Physical diff --git a/jdk/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java b/jdk/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java index d875da33991..81a90f5021d 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java +++ b/jdk/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java @@ -42,7 +42,7 @@ package sun.font; * this appears to cause problems. */ -public final class CompositeGlyphMapper extends CharToGlyphMapper { +public class CompositeGlyphMapper extends CharToGlyphMapper { public static final int SLOTMASK = 0xff000000; public static final int GLYPHMASK = 0x00ffffff; diff --git a/jdk/src/java.desktop/share/classes/sun/font/Font2D.java b/jdk/src/java.desktop/share/classes/sun/font/Font2D.java index e1c7c912e3d..7e7edc3e8cc 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/Font2D.java +++ b/jdk/src/java.desktop/share/classes/sun/font/Font2D.java @@ -461,10 +461,17 @@ public abstract class Font2D { * to check the font class before attempting to run, rather than needing * to promote this method up from TrueTypeFont */ - byte[] getTableBytes(int tag) { + protected byte[] getTableBytes(int tag) { return null; } + /* implemented for fonts backed by an sfnt that has + * OpenType or AAT layout tables. + */ + protected long getLayoutTableCache() { + return 0L; + } + /* for layout code */ protected long getUnitsPerEm() { return 2048; diff --git a/jdk/src/java.desktop/share/classes/sun/font/FontSubstitution.java b/jdk/src/java.desktop/share/classes/sun/font/FontSubstitution.java new file mode 100644 index 00000000000..47dfc6f6c5e --- /dev/null +++ b/jdk/src/java.desktop/share/classes/sun/font/FontSubstitution.java @@ -0,0 +1,38 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package sun.font; + + + +/** + * Interface that indicates a Font2D that is not a Composite but has the + * property that it internally behaves like one, substituting glyphs + * from another font at render time. + * In this case the Font must provide a way to behave like a regular + * composite when that behaviour is not wanted. + */ +public interface FontSubstitution { + public CompositeFont getCompositeFont2D(); +} diff --git a/jdk/src/java.desktop/share/classes/sun/font/GlyphLayout.java b/jdk/src/java.desktop/share/classes/sun/font/GlyphLayout.java index 0129d225730..143795e7b6a 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/GlyphLayout.java +++ b/jdk/src/java.desktop/share/classes/sun/font/GlyphLayout.java @@ -408,6 +408,9 @@ public final class GlyphLayout { int lang = -1; // default for now Font2D font2D = FontUtilities.getFont2D(font); + if (font2D instanceof FontSubstitution) { + font2D = ((FontSubstitution)font2D).getCompositeFont2D(); + } _textRecord.init(text, offset, lim, min, max); int start = offset; diff --git a/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java b/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java index d48c99a6781..06abf9de163 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java +++ b/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java @@ -1124,6 +1124,9 @@ public class StandardGlyphVector extends GlyphVector { private void initFontData() { font2D = FontUtilities.getFont2D(font); + if (font2D instanceof FontSubstitution) { + font2D = ((FontSubstitution)font2D).getCompositeFont2D(); + } float s = font.getSize2D(); if (font.isTransformed()) { ftx = font.getTransform(); @@ -1742,7 +1745,12 @@ public class StandardGlyphVector extends GlyphVector { aa, fm); // Get the strike via the handle. Shouldn't matter // if we've invalidated the font but its an extra precaution. - FontStrike strike = sgv.font2D.handle.font2D.getStrike(desc); // !!! getStrike(desc, false) + // do we want the CompFont from CFont here ? + Font2D f2d = sgv.font2D; + if (f2d instanceof FontSubstitution) { + f2d = ((FontSubstitution)f2d).getCompositeFont2D(); + } + FontStrike strike = f2d.handle.font2D.getStrike(desc); // !!! getStrike(desc, false) return new GlyphStrike(sgv, strike, dx, dy); } diff --git a/jdk/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java b/jdk/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java index 47f0022a443..a62b1e5fe21 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java +++ b/jdk/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java @@ -155,10 +155,7 @@ public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory Point2D.Float pt, GVData data) { Font2D font = key.font(); FontStrike strike = font.getStrike(desc); - long layoutTables = 0; - if (font instanceof TrueTypeFont) { - layoutTables = ((TrueTypeFont) font).getLayoutTableCache(); - } + long layoutTables = font.getLayoutTableCache(); nativeLayout(font, strike, mat, gmask, baseIndex, tr.text, tr.start, tr.limit, tr.min, tr.max, key.script(), key.lang(), typo_flags, pt, data, diff --git a/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java b/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java index 0e8647cc734..f3bb84529da 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java +++ b/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java @@ -874,8 +874,8 @@ public class TrueTypeFont extends FileFont { } } - /* NB: is it better to move declaration to Font2D? */ - long getLayoutTableCache() { + @Override + protected long getLayoutTableCache() { try { return getScaler().getLayoutTableCache(); } catch(FontScalerException fe) { @@ -884,7 +884,7 @@ public class TrueTypeFont extends FileFont { } @Override - byte[] getTableBytes(int tag) { + protected byte[] getTableBytes(int tag) { ByteBuffer buffer = getTableBuffer(tag); if (buffer == null) { return null; diff --git a/jdk/test/java/awt/font/TextLayout/OSXLigatureTest.java b/jdk/test/java/awt/font/TextLayout/OSXLigatureTest.java new file mode 100644 index 00000000000..b1d2d797dae --- /dev/null +++ b/jdk/test/java/awt/font/TextLayout/OSXLigatureTest.java @@ -0,0 +1,82 @@ +/* + * 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. + */ + +/* + * @test + * @bug 7162125 + * @summary Test ligatures form on OS X. + */ + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.awt.font.TextAttribute; +import java.util.HashMap; +import java.util.Map; + +public class OSXLigatureTest { + + public static void main(String[] args) { + if (!System.getProperty("os.name").startsWith("Mac")) { + return; + } + String ligStr = "ffi"; + int w = 50, h = 50; + + BufferedImage bi1 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + Graphics2D bi1Graphics = bi1.createGraphics(); + bi1Graphics.setColor(Color.white); + bi1Graphics.fillRect(0, 0, w, h); + bi1Graphics.setColor(Color.black); + Font noLigFont = new Font("Gill Sans", Font.PLAIN, 30); + bi1Graphics.setFont(noLigFont); + bi1Graphics.drawString(ligStr, 10, 40); + + BufferedImage bi2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + Graphics2D bi2Graphics = bi2.createGraphics(); + bi2Graphics.setColor(Color.white); + bi2Graphics.fillRect(0, 0, w, h); + bi2Graphics.setColor(Color.black); + Map attributes = new HashMap<>(); + attributes.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON); + Font ligFont = noLigFont.deriveFont(attributes); + bi2Graphics.setFont(ligFont); + bi2Graphics.drawString(ligStr, 10, 40); + + boolean same = true; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int c1 = bi1.getRGB(x, y); + int c2 = bi2.getRGB(x, y); + same &= (c1 == c2); + } + if (!same) { + break; + } + } + if (same) { + throw new RuntimeException("Images do not differ - no ligature"); + } + } +} From 2946748843d9bdbf65c673f3a21b779ab3eac74c Mon Sep 17 00:00:00 2001 From: Rajeev Chamyal Date: Tue, 17 Nov 2015 13:14:26 +0400 Subject: [PATCH 035/199] 8030099: Memory usage of java process increases after pressing start button in test window Reviewed-by: prr, serb --- .../sun/awt/shell/Win32ShellFolder2.java | 7 +- .../sun/awt/shell/ShellFolderMemoryLeak.java | 226 ++++++++++++++++++ 2 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 jdk/test/sun/awt/shell/ShellFolderMemoryLeak.java diff --git a/jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java b/jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java index 9539b981709..8e32e39acc8 100644 --- a/jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java +++ b/jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java @@ -141,6 +141,9 @@ final class Win32ShellFolder2 extends ShellFolder { public static final int SHGDN_FORADDRESSBAR = 0x4000; public static final int SHGDN_FORPARSING = 0x8000; + /** The referent to be registered with the Disposer. */ + private Object disposerReferent = new Object(); + // Values for system call LoadIcon() public enum SystemIcon { IDI_APPLICATION(32512), @@ -297,7 +300,7 @@ final class Win32ShellFolder2 extends ShellFolder { } }, InterruptedException.class); - sun.java2d.Disposer.addRecord(this, disposer); + sun.java2d.Disposer.addObjectRecord(disposerReferent, disposer); } @@ -309,7 +312,7 @@ final class Win32ShellFolder2 extends ShellFolder { this.isLib = isLib; this.disposer.pIShellFolder = pIShellFolder; this.disposer.relativePIDL = relativePIDL; - sun.java2d.Disposer.addRecord(this, disposer); + sun.java2d.Disposer.addObjectRecord(disposerReferent, disposer); } diff --git a/jdk/test/sun/awt/shell/ShellFolderMemoryLeak.java b/jdk/test/sun/awt/shell/ShellFolderMemoryLeak.java new file mode 100644 index 00000000000..68a18e0e42c --- /dev/null +++ b/jdk/test/sun/awt/shell/ShellFolderMemoryLeak.java @@ -0,0 +1,226 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8030099 + * @summary Memory usage of java process increases + after calling Win32ShellFolder:listFiles + multiple times on some directory with + large number of files/folders + * @modules java.desktop/sun.awt + * @requires (os.family == "windows") + * @run main/timeout=1000 ShellFolderMemoryLeak + */ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.logging.Level; +import java.util.logging.Logger; +import sun.awt.shell.ShellFolder; + +public class ShellFolderMemoryLeak { + + private final static String tempDir = System.getProperty("java.io.tmpdir"); + private static Process process; + public static void main(String[] args) throws Exception { + if (args.length == 0) { + boolean testResultParallel + = createChildProcessWithParallelCollector(); + String result = ""; + if (!testResultParallel) { + result = "Test failed with Parallel collector"; + } + boolean testResultDefault + = createChildProcessWithDefaultCollector(); + if (!testResultDefault && !testResultParallel) { + result += " and with default collector both."; + } else if (!testResultDefault) { + result = "Test failed with default collector"; + } + if (!"".equals(result)) { + throw new RuntimeException(result); + } + } else { + testListFile(args[args.length - 1]); + } + } + + public static boolean createChildProcessWithDefaultCollector() + throws Exception { + String testDirectory = "TestDirectory1"; + testDirectory = tempDir + testDirectory +File.separator; + createTestData(testDirectory); + return runProcess("", testDirectory); + } + + public static boolean createChildProcessWithParallelCollector() + throws Exception { + String testDirectory = "TestDirectory2"; + testDirectory = tempDir + testDirectory +File.separator; + createTestData(testDirectory); + return runProcess(" -XX:+UseParallelGC", testDirectory); + } + + public static boolean runProcess(String arg1, String arg2) throws Exception { + String javaPath = System.getProperty("java.home"); + String classPathDir = System.getProperty("java.class.path"); + + //creating java process which run same class with different Xmx value + String command = javaPath + File.separator + "bin" + File.separator + + "java -Xmx256M" + arg1 + " -cp " + + classPathDir + + " ShellFolderMemoryLeak " + arg2; + process = Runtime.getRuntime().exec(command); + BufferedReader input = null; + InputStream errorStream = null; + String line = null; + try { + int exitVal = process.waitFor(); + input = new BufferedReader(new InputStreamReader( + process.getInputStream())); + while ((line = input.readLine()) != null) { + } + errorStream = process.getErrorStream(); + if (checkExceptions(errorStream) || exitVal != 0) { + return false; + } + } catch (IllegalThreadStateException e) { + throw new RuntimeException(e); + } finally { + if (input != null) { + input.close(); + } + if (errorStream != null) { + errorStream.close(); + } + process.destroy(); + } + return true; + } + + public static boolean checkExceptions(InputStream in) throws IOException { + String tempString; + int count = in.available(); + boolean exception = false; + while (count > 0) { + byte[] b = new byte[count]; + in.read(b); + tempString = new String(b); + if (!exception) { + exception = tempString.contains("RunTimeException"); + } + count = in.available(); + } + return exception; + } + + private static void createTestData(String testDirectory) { + String folder = "folder_"; + File testFolder = new File(testDirectory); + if (testFolder.exists()) { + clearTestData(testDirectory); + } else { + if (testFolder.mkdir()) { + for (int inx = 0; inx < 100; inx++) { + new File(testFolder + File.separator + folder + inx).mkdir(); + } + } else { + throw new RuntimeException("Failed to create testDirectory"); + } + } + } + + public static void deleteDirectory(File file) + throws IOException { + + if (file.isDirectory()) { + if (file.list().length == 0) { + file.delete(); + } else { + String files[] = file.list(); + for (String temp : files) { + File fileDelete = new File(file, temp); + deleteDirectory(fileDelete); + } + if (file.list().length == 0) { + file.delete(); + } + } + } + } + + private static void testListFile(String testDirectory) { + try { + int mb = 1024 * 1024; + ShellFolder folder = ShellFolder.getShellFolder( + new File(testDirectory)); + Runtime instance = Runtime.getRuntime(); + + //Memory used before calling listFiles + long startmem = instance.totalMemory() - instance.freeMemory(); + long start = System.currentTimeMillis(); + long endmem = 0; + + //Calling listFiles for 5 minutes with sleep of 10 ms. + while ((System.currentTimeMillis() - start) < 300000) { + try { + folder.listFiles(); + Thread.sleep(10); + endmem = instance.totalMemory() - instance.freeMemory(); + } catch (InterruptedException ex) { + Logger.getLogger(ShellFolderMemoryLeak.class.getName()) + .log(Level.SEVERE, "InterruptedException", ex); + } + } + + //Total increase in memory after 5 minutes + long result = (endmem - startmem) / mb; + + if (result > 100) { + clearTestData(testDirectory); + throw new RuntimeException("Test Failed"); + } + clearTestData(testDirectory); + } catch (FileNotFoundException ex) { + if(process != null && process.isAlive()) { + process.destroy(); + } + Logger.getLogger(ShellFolderMemoryLeak.class.getName()) + .log(Level.SEVERE, "File Not Found Exception", ex); + } + } + + private static void clearTestData(String testDirectory) { + File testFolder = new File(testDirectory); + try { + deleteDirectory(testFolder); + } catch (IOException ex) { + Logger.getLogger(ShellFolderMemoryLeak.class.getName()) + .log(Level.SEVERE, "Unable to delete files", ex); + } + } +} \ No newline at end of file From 2e94af3f3d7010e89852721d1fda7a226e8ffb6c Mon Sep 17 00:00:00 2001 From: Avik Niyogi Date: Tue, 17 Nov 2015 19:09:37 +0400 Subject: [PATCH 036/199] 7124218: Space should select cell in the JTable Reviewed-by: rchamyal, alexsch --- .../JTable/7124218/SelectEditTableCell.java | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 jdk/test/javax/swing/JTable/7124218/SelectEditTableCell.java diff --git a/jdk/test/javax/swing/JTable/7124218/SelectEditTableCell.java b/jdk/test/javax/swing/JTable/7124218/SelectEditTableCell.java new file mode 100644 index 00000000000..6191ddf04f8 --- /dev/null +++ b/jdk/test/javax/swing/JTable/7124218/SelectEditTableCell.java @@ -0,0 +1,201 @@ +/* + * 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. + */ + +/* @test + * @bug 7124218 + * @summary verifies different behaviour of SPACE and ENTER in JTable + * @library ../../regtesthelpers + * @build Util + * @run main SelectEditTableCell + */ +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import javax.swing.DefaultListSelectionModel; +import javax.swing.JFrame; +import javax.swing.JTable; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +public class SelectEditTableCell { + + private static JFrame frame; + private static JTable table; + private static Robot robot; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.delay(2000); + UIManager.LookAndFeelInfo[] lookAndFeelArray + = UIManager.getInstalledLookAndFeels(); + for (UIManager.LookAndFeelInfo lookAndFeelItem : lookAndFeelArray) { + executeCase(lookAndFeelItem.getClassName()); + } + + } + + private static void executeCase(String lookAndFeelString) throws Exception { + if (tryLookAndFeel(lookAndFeelString)) { + createUI(lookAndFeelString); + robot.delay(2000); + runTestCase(); + robot.delay(2000); + cleanUp(); + robot.delay(2000); + } + + } + + private static void createUI(final String lookAndFeelString) + throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + String[][] data = {{"Foo"}}; + String[] cols = {"One"}; + table = new JTable(data, cols); + table.setSelectionMode( + DefaultListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + frame = new JFrame(lookAndFeelString); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(table); + frame.pack(); + frame.setSize(500, frame.getSize().height); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.toFront(); + } + }); + } + + private static void runTestCase() throws Exception { + Point centerPoint; + centerPoint = Util.getCenterPoint(table); + LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); + robot.mouseMove(centerPoint.x, centerPoint.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + table.clearSelection(); + if (table.isEditing() || table.isCellSelected(0, 0)) { + // assumption is bad, bail + frame.dispose(); + throw new AssertionError("Failed assumption: assumed no" + + "editing and no selection."); + } + } + }); + robot.waitForIdle(); + int fetchKeyCode; + keyTap(fetchKeyCode = isMac(lookAndFeel) + ? KeyEvent.VK_ENTER : KeyEvent.VK_SPACE); + final int keyCode = fetchKeyCode; + robot.waitForIdle(); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (!table.isCellSelected(0, 0)) { + frame.dispose(); + throw new RuntimeException(((keyCode == KeyEvent.VK_ENTER) + ? "Enter" : "Space") + + " should select cell"); + } + } + }); + robot.waitForIdle(); + keyTap(KeyEvent.VK_SPACE); + robot.waitForIdle(); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (!table.isEditing()) { + frame.dispose(); + throw new RuntimeException("Space should start editing"); + } + table.getCellEditor().cancelCellEditing(); + table.clearSelection(); + if (table.isEditing() || table.isCellSelected(0, 0)) { + // assumption is bad, bail + frame.dispose(); + throw new AssertionError("Failed assumption: assumed no " + + "editing and no selection."); + } + } + }); + robot.waitForIdle(); + // hitting a letter key will start editing + keyTap(KeyEvent.VK_A); + keyTap(KeyEvent.VK_SPACE); + keyTap(KeyEvent.VK_A); + robot.waitForIdle(); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (table.isCellSelected(0, 0)) { + frame.dispose(); + throw new RuntimeException("Space should not select when " + + "already editing."); + } + } + }); + } + + private static void cleanUp() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame.dispose(); + } + }); + } + + private static boolean isMac(LookAndFeel lookAndFeel) { + + return lookAndFeel.toString().toLowerCase().contains("mac"); + } + + private static void keyTap(int keyCode) { + robot.keyPress(keyCode); + robot.keyRelease(keyCode); + } + + private static boolean tryLookAndFeel(String lookAndFeelString) + throws Exception { + try { + UIManager.setLookAndFeel( + lookAndFeelString); + + } catch (UnsupportedLookAndFeelException + | ClassNotFoundException + | InstantiationException + | IllegalAccessException e) { + return false; + } + return true; + } +} From 1e176777dbe507ea94e838d858733a7cfaaade7f Mon Sep 17 00:00:00 2001 From: Avik Niyogi Date: Tue, 17 Nov 2015 19:29:04 +0400 Subject: [PATCH 037/199] 8132770: Test javax/swing/JRadioButton/FocusTraversal/FocusTraversal.java fails in MacOSX Reviewed-by: rchamyal, alexsch --- .../FocusTraversal/FocusTraversal.java | 170 +++++++++++++----- 1 file changed, 125 insertions(+), 45 deletions(-) diff --git a/jdk/test/javax/swing/JRadioButton/FocusTraversal/FocusTraversal.java b/jdk/test/javax/swing/JRadioButton/FocusTraversal/FocusTraversal.java index 53e975447d4..34a7cfe0206 100644 --- a/jdk/test/javax/swing/JRadioButton/FocusTraversal/FocusTraversal.java +++ b/jdk/test/javax/swing/JRadioButton/FocusTraversal/FocusTraversal.java @@ -22,43 +22,82 @@ */ /* @test - @bug 8129940 - @summary JRadioButton does not honor non-standard FocusTraversalKeys - @author Semyon Sadetsky - */ - -import javax.swing.*; -import java.awt.*; + @bug 8129940 8132770 + @summary JRadioButton should run custom FocusTraversalKeys for all LaFs + @run main FocusTraversal + */ +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.KeyboardFocusManager; +import java.awt.Robot; import java.awt.event.KeyEvent; import java.util.HashSet; import java.util.Set; +import javax.swing.ButtonGroup; +import javax.swing.FocusManager; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; public class FocusTraversal { private static JFrame frame; private static JRadioButton a; + private static JRadioButton b; + private static JRadioButton c; private static JRadioButton d; private static JTextField next; private static JTextField prev; + private static Robot robot; public static void main(String[] args) throws Exception { + + robot = new Robot(); + robot.delay(2000); + UIManager.LookAndFeelInfo[] lookAndFeelArray + = UIManager.getInstalledLookAndFeels(); + for (UIManager.LookAndFeelInfo lookAndFeelItem : lookAndFeelArray) { + executeCase(lookAndFeelItem.getClassName()); + } + } + + private static void executeCase(String lookAndFeelString) + throws Exception { + if (tryLookAndFeel(lookAndFeelString)) { + createUI(lookAndFeelString); + robot.delay(2000); + runTestCase(); + robot.delay(2000); + cleanUp(); + robot.delay(2000); + } + } + + private static void createUI(final String lookAndFeelString) + throws Exception { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { - frame = new JFrame("FocusTraversalTest"); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.setUndecorated(true); - Set keystrokes = new HashSet(); keystrokes.add(KeyStroke.getKeyStroke("TAB")); keystrokes.add(KeyStroke.getKeyStroke("ENTER")); + frame = new JFrame("FocusTraversalTest " + lookAndFeelString); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setUndecorated(true); frame.setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keystrokes); a = new JRadioButton("a"); - JRadioButton b = new JRadioButton("b"); - JRadioButton c = new JRadioButton("c"); + b = new JRadioButton("b"); + c = new JRadioButton("c"); d = new JRadioButton("d"); ButtonGroup radioButtonGroup = new ButtonGroup(); @@ -84,61 +123,102 @@ public class FocusTraversal { frame.add(root); frame.pack(); + frame.setLocationRelativeTo(null); frame.setVisible(true); + frame.toFront(); } }); + } + private static void runTestCase() throws Exception { + LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); + focusOn(a); + if (isExcludedLookAndFeel(lookAndFeel)) { + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + robot.waitForIdle(); + isFocusOwner(b, "forward"); + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.waitForIdle(); + isFocusOwner(a, "backward"); + + } else { + + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + robot.waitForIdle(); + isFocusOwner(next, "forward"); + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.waitForIdle(); + isFocusOwner(d, "backward"); + } + + } + + private static boolean isExcludedLookAndFeel(LookAndFeel lookAndFeel) { + + return lookAndFeel.toString().toLowerCase().contains("aqua") + || lookAndFeel.toString().toLowerCase().contains("nimbus") + || lookAndFeel.toString().toLowerCase().contains("gtk"); + } + + private static void focusOn(Component component) + throws Exception { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { - a.requestFocus(); + component.requestFocusInWindow(); } }); + } - Robot robot = new Robot(); - robot.waitForIdle(); - - robot.setAutoDelay(200); - - robot.keyPress(KeyEvent.VK_ENTER); - robot.keyRelease(KeyEvent.VK_ENTER); - robot.waitForIdle(); - + private static void isFocusOwner(Component queriedFocusOwner, + String direction) + throws Exception { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { - Component focusOwner = - FocusManager.getCurrentManager().getFocusOwner(); - if (focusOwner != next) { + Component actualFocusOwner + = FocusManager.getCurrentManager().getFocusOwner(); + if (actualFocusOwner != queriedFocusOwner) { + frame.dispose(); throw new RuntimeException( - "Focus component is wrong after forward key " + focusOwner); + "Focus component is wrong after " + direction + + " direction "); + } } }); + } - robot.keyPress(KeyEvent.VK_SHIFT); - robot.keyPress(KeyEvent.VK_TAB); - robot.keyRelease(KeyEvent.VK_TAB); - robot.keyRelease(KeyEvent.VK_SHIFT); - robot.waitForIdle(); + private static boolean tryLookAndFeel(String lookAndFeelString) + throws Exception { + + try { + UIManager.setLookAndFeel( + lookAndFeelString); + + } catch (UnsupportedLookAndFeelException + | ClassNotFoundException + | InstantiationException + | IllegalAccessException e) { + return false; + } + return true; + } + + private static void cleanUp() throws Exception { SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - Component focusOwner = - FocusManager.getCurrentManager().getFocusOwner(); - if (focusOwner != d) { - throw new RuntimeException( - "Focus component is wrong after backward key " + focusOwner); - } - } - }); - SwingUtilities.invokeLater(new Runnable() { @Override public void run() { frame.dispose(); } }); - System.out.println("ok"); - } } From 73b1044258a2bb0ac6a89c5dd10254ed7599c274 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 17 Nov 2015 19:15:48 +0300 Subject: [PATCH 038/199] 8039412: Stack overflow on Linux using DialogTypeSelection.NATIVE Reviewed-by: prr, rchamyal --- .../classes/sun/print/RasterPrinterJob.java | 14 ++++- .../PrinterJob/PageDlgStackOverflowTest.java | 53 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 jdk/test/java/awt/print/PrinterJob/PageDlgStackOverflowTest.java diff --git a/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java b/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java index b3225679369..e5024b20764 100644 --- a/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java +++ b/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java @@ -740,7 +740,19 @@ public abstract class RasterPrinterJob extends PrinterJob { } updatePageAttributes(service, page); - PageFormat newPage = pageDialog(attributes); + PageFormat newPage = null; + DialogTypeSelection dts = + (DialogTypeSelection)attributes.get(DialogTypeSelection.class); + if (dts == DialogTypeSelection.NATIVE) { + // Remove DialogTypeSelection.NATIVE to prevent infinite loop in + // RasterPrinterJob. + attributes.remove(DialogTypeSelection.class); + newPage = pageDialog(attributes); + // restore attribute + attributes.add(DialogTypeSelection.NATIVE); + } else { + newPage = pageDialog(attributes); + } if (newPage == null) { return page; diff --git a/jdk/test/java/awt/print/PrinterJob/PageDlgStackOverflowTest.java b/jdk/test/java/awt/print/PrinterJob/PageDlgStackOverflowTest.java new file mode 100644 index 00000000000..ffe695d6f48 --- /dev/null +++ b/jdk/test/java/awt/print/PrinterJob/PageDlgStackOverflowTest.java @@ -0,0 +1,53 @@ +/* + * 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.print.PrinterJob; +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.standard.DialogTypeSelection; + +/** + * @test + * @bug 8039412 + * @run main/manual PageDlgStackOverflowTest + * @summary Calling pageDialog() after printDialog with + * DialogTypeSelection.NATIVE should not result in StackOverflowError + */ +public class PageDlgStackOverflowTest { + + public static void main(String args[]) { + PrinterJob job = PrinterJob.getPrinterJob(); + if (job == null) { + return; + } + PrintRequestAttributeSet pSet = + new HashPrintRequestAttributeSet(); + pSet.add(DialogTypeSelection.NATIVE); + job.printDialog(pSet); + try { + job.pageDialog(pSet); + } catch (StackOverflowError e) { + throw new RuntimeException("StackOverflowError is thrown"); + } + } +} + From 18cd974bce1c4802a8b1841a0a609145a3f728b3 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 18 Nov 2015 00:20:15 +0300 Subject: [PATCH 039/199] 8067059: PrinterJob.pageDialog() with DialogSelectionType.NATIVE returns a PageFormat when cancelled Reviewed-by: jgodinez, prr --- .../classes/sun/print/RasterPrinterJob.java | 11 +++- .../java/awt/print/PrinterJob/PageDlgApp.java | 65 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 jdk/test/java/awt/print/PrinterJob/PageDlgApp.java diff --git a/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java b/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java index e5024b20764..585e6e495b8 100644 --- a/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java +++ b/jdk/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java @@ -778,8 +778,15 @@ public abstract class RasterPrinterJob extends PrinterJob { // Check for native, note that default dialog is COMMON. if (dlg == DialogTypeSelection.NATIVE) { PrintService pservice = getPrintService(); - PageFormat page = pageDialog(attributeToPageFormat(pservice, - attributes)); + PageFormat pageFrmAttrib = attributeToPageFormat(pservice, + attributes); + PageFormat page = pageDialog(pageFrmAttrib); + + // If user cancels the dialog, pageDialog() will return the original + // page object and as per spec, we should return null in that case. + if (page == pageFrmAttrib) { + return null; + } updateAttributesWithPageFormat(pservice, page, attributes); return page; } diff --git a/jdk/test/java/awt/print/PrinterJob/PageDlgApp.java b/jdk/test/java/awt/print/PrinterJob/PageDlgApp.java new file mode 100644 index 00000000000..e07e8ffd6cf --- /dev/null +++ b/jdk/test/java/awt/print/PrinterJob/PageDlgApp.java @@ -0,0 +1,65 @@ +/* + * 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.Component; +import java.awt.print.PrinterJob; +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.standard.DialogTypeSelection; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +/** + * @test + * @bug 8067059 + * @run main/manual PageDlgApp + * @summary Test if cancelling dialog returns null when + * PrinterJob.pageDialog() with DialogSelectionType.NATIVE is called + */ +public class PageDlgApp { + + public static void main(String[] args) throws Exception { + + String[] instructions = + { + "Visual inspection of the dialog is needed. ", + "It should be a Printer Job Setup Dialog", + "Do nothing except Cancel", + "You must NOT press OK", + }; + SwingUtilities.invokeAndWait(() -> { + JOptionPane.showMessageDialog( + (Component) null, + instructions, + "information", JOptionPane.INFORMATION_MESSAGE); + }); + PrinterJob pj = PrinterJob.getPrinterJob(); + PrintRequestAttributeSet pSet = new HashPrintRequestAttributeSet(); + pSet.add(DialogTypeSelection.NATIVE); + if ((pj.pageDialog(pSet)) != null) { + throw + new RuntimeException("PrinterJob.pageDialog(PrintRequestAttributeSet)" + + " does not return null when dialog is cancelled"); + } + } +} + From b0439e6dbfc70f311e54464dc25657bdb09fa9c5 Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Wed, 18 Nov 2015 19:13:42 +0400 Subject: [PATCH 040/199] 8081411: Add an API for painting an icon with a SynthContext Reviewed-by: serb, azvegint --- .../java/swing/plaf/gtk/GTKIconFactory.java | 1 + .../com/sun/java/swing/plaf/gtk/GTKStyle.java | 2 +- .../javax/swing/plaf/nimbus/NimbusIcon.java | 4 +- .../swing/plaf/nimbus/NimbusLookAndFeel.java | 2 +- .../swing/plaf/synth/SynthGraphicsUtils.java | 69 +++++++- .../javax/swing/plaf/synth/SynthIcon.java | 82 ++++++++++ .../swing/plaf/synth/SynthLookAndFeel.java | 4 + .../plaf/synth/SynthMenuItemLayoutHelper.java | 13 +- .../swing/plaf/synth/SynthToolBarUI.java | 21 ++- .../javax/swing/plaf/synth/SynthTreeUI.java | 21 ++- .../sun/swing/plaf/synth/SynthIcon.java | 126 --------------- .../swing/plaf/synth/8081411/bug8081411.java | 147 ++++++++++++++++++ 12 files changed, 326 insertions(+), 166 deletions(-) create mode 100644 jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthIcon.java delete mode 100644 jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthIcon.java create mode 100644 jdk/test/javax/swing/plaf/synth/8081411/bug8081411.java diff --git a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java index 44ccd4925ce..f83112c6537 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java +++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java @@ -24,6 +24,7 @@ */ package com.sun.java.swing.plaf.gtk; +import javax.swing.plaf.synth.SynthIcon; import java.util.*; import javax.swing.plaf.synth.*; import java.awt.*; diff --git a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java index 63322a6396a..160540aad78 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java +++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java @@ -36,7 +36,7 @@ import javax.swing.plaf.synth.*; import sun.awt.AppContext; import sun.awt.UNIXToolkit; import sun.swing.SwingUtilities2; -import sun.swing.plaf.synth.SynthIcon; +import javax.swing.plaf.synth.SynthIcon; import com.sun.java.swing.plaf.gtk.GTKEngine.WidgetType; import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING; diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusIcon.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusIcon.java index 696f0a220f8..41e055908b4 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusIcon.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusIcon.java @@ -25,7 +25,7 @@ package javax.swing.plaf.nimbus; import javax.swing.Painter; -import sun.swing.plaf.synth.SynthIcon; +import javax.swing.plaf.synth.SynthIcon; import javax.swing.plaf.synth.SynthContext; import javax.swing.*; @@ -37,7 +37,7 @@ import javax.swing.plaf.UIResource; * An icon that delegates to a painter. * @author rbair */ -class NimbusIcon extends SynthIcon { +class NimbusIcon implements SynthIcon { private int width; private int height; private String prefix; diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java index 71e00249752..c46c6b2e5b1 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java @@ -50,7 +50,7 @@ import javax.swing.border.TitledBorder; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.ColorUIResource; import sun.swing.ImageIconUIResource; -import sun.swing.plaf.synth.SynthIcon; +import javax.swing.plaf.synth.SynthIcon; import sun.swing.plaf.GTKKeybindings; import sun.swing.plaf.WindowsKeybindings; import sun.security.action.GetPropertyAction; diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java index bf66cee2bcb..e91257ed0f0 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java @@ -31,7 +31,6 @@ import java.awt.*; import javax.swing.*; import javax.swing.plaf.basic.BasicHTML; import javax.swing.text.*; -import sun.swing.plaf.synth.*; /** * Wrapper for primitive graphics calls. @@ -287,8 +286,8 @@ public class SynthGraphicsUtils { return new Dimension(dx, dy); } else if ((text == null) || ((icon != null) && (font == null))) { - return new Dimension(SynthIcon.getIconWidth(icon, ss) + dx, - SynthIcon.getIconHeight(icon, ss) + dy); + return new Dimension(getIconWidth(icon, ss) + dx, + getIconHeight(icon, ss) + dy); } else { FontMetrics fm = c.getFontMetrics(font); @@ -404,7 +403,7 @@ public class SynthGraphicsUtils { paintIconR.x += textOffset; } paintIconR.y += textOffset; - SynthIcon.paintIcon(icon, ss, g, paintIconR.x, paintIconR.y, + paintIcon(icon, ss, g, paintIconR.x, paintIconR.y, paintIconR.width, paintIconR.height); g.setColor(color); } @@ -423,6 +422,62 @@ public class SynthGraphicsUtils { } } + /** + * Returns the icon's width. + * The {@code getIconWidth(context)} method is called for {@code SynthIcon}. + * + * @param icon the icon + * @param context {@code SynthContext} requesting the icon, may be null. + * @return an int specifying the width of the icon. + */ + public static int getIconWidth(Icon icon, SynthContext context) { + if (icon == null) { + return 0; + } + if (icon instanceof SynthIcon) { + return ((SynthIcon) icon).getIconWidth(context); + } + return icon.getIconWidth(); + } + + /** + * Returns the icon's height. + * The {@code getIconHeight(context)} method is called for {@code SynthIcon}. + * + * @param icon the icon + * @param context {@code SynthContext} requesting the icon, may be null. + * @return an int specifying the height of the icon. + */ + public static int getIconHeight(Icon icon, SynthContext context) { + if (icon == null) { + return 0; + } + if (icon instanceof SynthIcon) { + return ((SynthIcon) icon).getIconHeight(context); + } + return icon.getIconHeight(); + } + + /** + * Paints the icon. The {@code paintIcon(context, g, x, y, width, height)} + * method is called for {@code SynthIcon}. + * + * @param icon the icon + * @param context identifies hosting region, may be null. + * @param g the graphics context + * @param x the x location to paint to + * @param y the y location to paint to + * @param width the width of the region to paint to, may be 0 + * @param height the height of the region to paint to, may be 0 + */ + public static void paintIcon(Icon icon, SynthContext context, Graphics g, + int x, int y, int width, int height) { + if (icon instanceof SynthIcon) { + ((SynthIcon) icon).paintIcon(context, g, x, y, width, height); + } else if (icon != null) { + icon.paintIcon(context.getComponent(), g, x, y); + } + } /** * A quick note about how preferred sizes are calculated... Generally @@ -561,7 +616,7 @@ public class SynthGraphicsUtils { if (icon != null) { Rectangle iconRect = lr.getIconRect(); - SynthIcon.paintIcon(icon, lh.getContext(), g, iconRect.x, + paintIcon(icon, lh.getContext(), g, iconRect.x, iconRect.y, iconRect.width, iconRect.height); } } @@ -571,7 +626,7 @@ public class SynthGraphicsUtils { MenuItemLayoutHelper.LayoutResult lr) { if (lh.getCheckIcon() != null) { Rectangle checkRect = lr.getCheckRect(); - SynthIcon.paintIcon(lh.getCheckIcon(), lh.getContext(), g, + paintIcon(lh.getCheckIcon(), lh.getContext(), g, checkRect.x, checkRect.y, checkRect.width, checkRect.height); } } @@ -610,7 +665,7 @@ public class SynthGraphicsUtils { MenuItemLayoutHelper.LayoutResult lr) { if (lh.getArrowIcon() != null) { Rectangle arrowRect = lr.getArrowRect(); - SynthIcon.paintIcon(lh.getArrowIcon(), lh.getContext(), g, + paintIcon(lh.getArrowIcon(), lh.getContext(), g, arrowRect.x, arrowRect.y, arrowRect.width, arrowRect.height); } } diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthIcon.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthIcon.java new file mode 100644 index 00000000000..cd5fb0dd5e2 --- /dev/null +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthIcon.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2002, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package javax.swing.plaf.synth; + +import java.awt.*; +import javax.swing.*; + +/** + * An icon that is passed a {@code SynthContext}. Subclasses need only implement + * the variants that take a {@code SynthContext}, but must be prepared for the + * {@code SynthContext} to be null. + * + * @author Scott Violet + */ +public interface SynthIcon extends Icon { + + /** + * Paints the icon at the specified location for the given synth context. + * + * @param context identifies hosting region, may be null. + * @param g the graphics context + * @param x the x location to paint to + * @param y the y location to paint to + * @param width the width of the region to paint to, may be 0 + * @param height the height of the region to paint to, may be 0 + */ + void paintIcon(SynthContext context, Graphics g, int x, int y, + int width, int height); + + /** + * Returns the icon's width for the given synth context. + * + * @param context {@code SynthContext} requesting the Icon, may be null. + * @return an int specifying the width of the icon. + */ + int getIconWidth(SynthContext context); + + /** + * Returns the icon's height for the given synth context. + * + * @param context {@code SynthContext} requesting the Icon, may be null. + * @return an int specifying the height of the icon. + */ + int getIconHeight(SynthContext context); + + @Override + default void paintIcon(Component c, Graphics g, int x, int y) { + paintIcon(null, g, x, y, getIconWidth(), getIconHeight()); + } + + @Override + default int getIconWidth() { + return getIconWidth(null); + } + + @Override + default int getIconHeight() { + return getIconHeight(null); + } +} diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java index fa4640dec73..3998b354ccb 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java @@ -53,6 +53,10 @@ import sun.swing.plaf.synth.*; * an example of providing your own SynthStyleFactory to * setStyleFactory. *

    + * {@link SynthIcon} interface provides + * {@code paintIcon(synthContext, graphics, x, y, width, height)} method that + * allows to draw the icon with the given {@code SynthContext}. + *

    * Warning: * This class implements {@link Serializable} as a side effect of it * extending {@link BasicLookAndFeel}. It is not intended to be serialized. diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java index 4cc1824a6eb..0e4a0afdcea 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java @@ -27,7 +27,6 @@ package javax.swing.plaf.synth; import sun.swing.StringUIClientPropertyKey; import sun.swing.MenuItemLayoutHelper; -import sun.swing.plaf.synth.SynthIcon; import javax.swing.*; import javax.swing.text.View; @@ -130,8 +129,8 @@ class SynthMenuItemLayoutHelper extends MenuItemLayoutHelper { protected void calcWidthsAndHeights() { // iconRect if (getIcon() != null) { - getIconSize().setWidth(SynthIcon.getIconWidth(getIcon(), context)); - getIconSize().setHeight(SynthIcon.getIconHeight(getIcon(), context)); + getIconSize().setWidth(SynthGraphicsUtils.getIconWidth(getIcon(), context)); + getIconSize().setHeight(SynthGraphicsUtils.getIconHeight(getIcon(), context)); } // accRect @@ -165,16 +164,16 @@ class SynthMenuItemLayoutHelper extends MenuItemLayoutHelper { // checkIcon if (getCheckIcon() != null) { getCheckSize().setWidth( - SynthIcon.getIconWidth(getCheckIcon(), context)); + SynthGraphicsUtils.getIconWidth(getCheckIcon(), context)); getCheckSize().setHeight( - SynthIcon.getIconHeight(getCheckIcon(), context)); + SynthGraphicsUtils.getIconHeight(getCheckIcon(), context)); } // arrowRect if (getArrowIcon() != null) { getArrowSize().setWidth( - SynthIcon.getIconWidth(getArrowIcon(), context)); + SynthGraphicsUtils.getIconWidth(getArrowIcon(), context)); getArrowSize().setHeight( - SynthIcon.getIconHeight(getArrowIcon(), context)); + SynthGraphicsUtils.getIconHeight(getArrowIcon(), context)); } } diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolBarUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolBarUI.java index 51d2fefd17e..bf2b9298973 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolBarUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolBarUI.java @@ -41,7 +41,6 @@ import javax.swing.JSeparator; import javax.swing.JToolBar; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolBarUI; -import sun.swing.plaf.synth.SynthIcon; /** * Provides the Synth L&F UI delegate for @@ -281,10 +280,10 @@ public class SynthToolBarUI extends BasicToolBarUI if (handleIcon != null && toolBar.isFloatable()) { int startX = toolBar.getComponentOrientation().isLeftToRight() ? 0 : toolBar.getWidth() - - SynthIcon.getIconWidth(handleIcon, context); - SynthIcon.paintIcon(handleIcon, context, g, startX, 0, - SynthIcon.getIconWidth(handleIcon, context), - SynthIcon.getIconHeight(handleIcon, context)); + SynthGraphicsUtils.getIconWidth(handleIcon, context); + SynthGraphicsUtils.paintIcon(handleIcon, context, g, startX, 0, + SynthGraphicsUtils.getIconWidth(handleIcon, context), + SynthGraphicsUtils.getIconHeight(handleIcon, context)); } SynthContext subcontext = getContext( @@ -358,7 +357,7 @@ public class SynthToolBarUI extends BasicToolBarUI if (tb.getOrientation() == JToolBar.HORIZONTAL) { dim.width = tb.isFloatable() ? - SynthIcon.getIconWidth(handleIcon, context) : 0; + SynthGraphicsUtils.getIconWidth(handleIcon, context) : 0; Dimension compDim; for (int i = 0; i < tb.getComponentCount(); i++) { Component component = tb.getComponent(i); @@ -370,7 +369,7 @@ public class SynthToolBarUI extends BasicToolBarUI } } else { dim.height = tb.isFloatable() ? - SynthIcon.getIconHeight(handleIcon, context) : 0; + SynthGraphicsUtils.getIconHeight(handleIcon, context) : 0; Dimension compDim; for (int i = 0; i < tb.getComponentCount(); i++) { Component component = tb.getComponent(i); @@ -396,7 +395,7 @@ public class SynthToolBarUI extends BasicToolBarUI if (tb.getOrientation() == JToolBar.HORIZONTAL) { dim.width = tb.isFloatable() ? - SynthIcon.getIconWidth(handleIcon, context) : 0; + SynthGraphicsUtils.getIconWidth(handleIcon, context) : 0; Dimension compDim; for (int i = 0; i < tb.getComponentCount(); i++) { Component component = tb.getComponent(i); @@ -408,7 +407,7 @@ public class SynthToolBarUI extends BasicToolBarUI } } else { dim.height = tb.isFloatable() ? - SynthIcon.getIconHeight(handleIcon, context) : 0; + SynthGraphicsUtils.getIconHeight(handleIcon, context) : 0; Dimension compDim; for (int i = 0; i < tb.getComponentCount(); i++) { Component component = tb.getComponent(i); @@ -449,7 +448,7 @@ public class SynthToolBarUI extends BasicToolBarUI if (tb.getOrientation() == JToolBar.HORIZONTAL) { int handleWidth = tb.isFloatable() ? - SynthIcon.getIconWidth(handleIcon, context) : 0; + SynthGraphicsUtils.getIconWidth(handleIcon, context) : 0; // Note: contentRect does not take insets into account // since it is used for determining the bounds that are @@ -500,7 +499,7 @@ public class SynthToolBarUI extends BasicToolBarUI } } else { int handleHeight = tb.isFloatable() ? - SynthIcon.getIconHeight(handleIcon, context) : 0; + SynthGraphicsUtils.getIconHeight(handleIcon, context) : 0; // See notes above regarding the use of insets contentRect.x = 0; diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthTreeUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthTreeUI.java index 6f00c947aa0..603540c6a58 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthTreeUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthTreeUI.java @@ -47,7 +47,6 @@ import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; -import sun.swing.plaf.synth.SynthIcon; /** * Provides the Synth L&F UI delegate for @@ -610,10 +609,10 @@ public class SynthTreeUI extends BasicTreeUI @Override protected void drawCentered(Component c, Graphics graphics, Icon icon, int x, int y) { - int w = SynthIcon.getIconWidth(icon, paintContext); - int h = SynthIcon.getIconHeight(icon, paintContext); + int w = SynthGraphicsUtils.getIconWidth(icon, paintContext); + int h = SynthGraphicsUtils.getIconHeight(icon, paintContext); - SynthIcon.paintIcon(icon, paintContext, graphics, + SynthGraphicsUtils.paintIcon(icon, paintContext, graphics, findCenteredX(x, w), y - h/2, w, h); } @@ -780,16 +779,16 @@ public class SynthTreeUI extends BasicTreeUI // To get the correct context we return an instance of this that fetches // the SynthContext as needed. // - private class ExpandedIconWrapper extends SynthIcon { + private class ExpandedIconWrapper implements SynthIcon { public void paintIcon(SynthContext context, Graphics g, int x, int y, int w, int h) { if (context == null) { context = getContext(tree); - SynthIcon.paintIcon(expandedIcon, context, g, x, y, w, h); + SynthGraphicsUtils.paintIcon(expandedIcon, context, g, x, y, w, h); context.dispose(); } else { - SynthIcon.paintIcon(expandedIcon, context, g, x, y, w, h); + SynthGraphicsUtils.paintIcon(expandedIcon, context, g, x, y, w, h); } } @@ -797,11 +796,11 @@ public class SynthTreeUI extends BasicTreeUI int width; if (context == null) { context = getContext(tree); - width = SynthIcon.getIconWidth(expandedIcon, context); + width = SynthGraphicsUtils.getIconWidth(expandedIcon, context); context.dispose(); } else { - width = SynthIcon.getIconWidth(expandedIcon, context); + width = SynthGraphicsUtils.getIconWidth(expandedIcon, context); } return width; } @@ -810,11 +809,11 @@ public class SynthTreeUI extends BasicTreeUI int height; if (context == null) { context = getContext(tree); - height = SynthIcon.getIconHeight(expandedIcon, context); + height = SynthGraphicsUtils.getIconHeight(expandedIcon, context); context.dispose(); } else { - height = SynthIcon.getIconHeight(expandedIcon, context); + height = SynthGraphicsUtils.getIconHeight(expandedIcon, context); } return height; } diff --git a/jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthIcon.java b/jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthIcon.java deleted file mode 100644 index 75c4e8358d2..00000000000 --- a/jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthIcon.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2002, 2003, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ -package sun.swing.plaf.synth; - -import javax.swing.plaf.synth.*; -import java.awt.*; -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.UIResource; - -/** - * An icon that is passed a SynthContext. Subclasses need only implement - * the variants that take a SynthContext, but must be prepared for the - * SynthContext to be null. - * - * @author Scott Violet - */ -public abstract class SynthIcon implements Icon { - public static int getIconWidth(Icon icon, SynthContext context) { - if (icon == null) { - return 0; - } - if (icon instanceof SynthIcon) { - return ((SynthIcon)icon).getIconWidth(context); - } - return icon.getIconWidth(); - } - - public static int getIconHeight(Icon icon, SynthContext context) { - if (icon == null) { - return 0; - } - if (icon instanceof SynthIcon) { - return ((SynthIcon)icon).getIconHeight(context); - } - return icon.getIconHeight(); - } - - public static void paintIcon(Icon icon, SynthContext context, Graphics g, - int x, int y, int w, int h) { - if (icon instanceof SynthIcon) { - ((SynthIcon)icon).paintIcon(context, g, x, y, w, h); - } - else if (icon != null) { - icon.paintIcon(context.getComponent(), g, x, y); - } - } - - /** - * Paints the icon at the specified location. - * - * @param context Identifies hosting region, may be null. - * @param x x location to paint to - * @param y y location to paint to - * @param w Width of the region to paint to, may be 0 - * @param h Height of the region to paint to, may be 0 - */ - public abstract void paintIcon(SynthContext context, Graphics g, int x, - int y, int w, int h); - - /** - * Returns the desired width of the Icon. - * - * @param context SynthContext requesting the Icon, may be null. - * @return Desired width of the icon. - */ - public abstract int getIconWidth(SynthContext context); - - /** - * Returns the desired height of the Icon. - * - * @param context SynthContext requesting the Icon, may be null. - * @return Desired height of the icon. - */ - public abstract int getIconHeight(SynthContext context); - - /** - * Paints the icon. This is a cover method for - * paintIcon(null, g, x, y, 0, 0) - */ - public void paintIcon(Component c, Graphics g, int x, int y) { - paintIcon(null, g, x, y, 0, 0); - } - - /** - * Returns the icon's width. This is a cover methods for - * getIconWidth(null). - * - * @return an int specifying the fixed width of the icon. - */ - public int getIconWidth() { - return getIconWidth(null); - } - - /** - * Returns the icon's height. This is a cover method for - * getIconHeight(null). - * - * @return an int specifying the fixed height of the icon. - */ - public int getIconHeight() { - return getIconHeight(null); - } -} diff --git a/jdk/test/javax/swing/plaf/synth/8081411/bug8081411.java b/jdk/test/javax/swing/plaf/synth/8081411/bug8081411.java new file mode 100644 index 00000000000..f155425c3e9 --- /dev/null +++ b/jdk/test/javax/swing/plaf/synth/8081411/bug8081411.java @@ -0,0 +1,147 @@ +/* + * 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.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import javax.swing.Icon; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; +import javax.swing.plaf.synth.Region; +import javax.swing.plaf.synth.SynthConstants; +import javax.swing.plaf.synth.SynthContext; +import javax.swing.plaf.synth.SynthGraphicsUtils; +import javax.swing.plaf.synth.SynthIcon; +import javax.swing.plaf.synth.SynthLookAndFeel; +import javax.swing.plaf.synth.SynthStyle; + +/* + * @test + * @bug 8081411 + * @summary Add an API for painting an icon with a SynthContext + * @author Alexander Scherbatiy + */ +public class bug8081411 { + + private static final Color TEST_COLOR = new Color(71, 71, 72); + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(bug8081411::testSynthIcon); + } + + private static void testSynthIcon() { + + if (!checkAndSetNimbusLookAndFeel()) { + return; + } + + JMenuItem menu = new JMenuItem(); + Icon subMenuIcon = UIManager.getIcon("Menu.arrowIcon"); + + if (!(subMenuIcon instanceof SynthIcon)) { + throw new RuntimeException("Icon is not a SynthIcon!"); + } + + Region region = SynthLookAndFeel.getRegion(menu); + SynthStyle style = SynthLookAndFeel.getStyle(menu, region); + SynthContext synthContext = new SynthContext(menu, region, style, SynthConstants.ENABLED); + + int width = SynthGraphicsUtils.getIconWidth(subMenuIcon, synthContext); + int height = SynthGraphicsUtils.getIconHeight(subMenuIcon, synthContext); + paintAndCheckIcon(subMenuIcon, synthContext, width, height); + + int newWidth = width * 17; + int newHeight = height * 37; + Icon centeredIcon = new CenteredSynthIcon((SynthIcon) subMenuIcon, + newWidth, newHeight); + paintAndCheckIcon(centeredIcon, synthContext, newWidth, newHeight); + } + + private static void paintAndCheckIcon(Icon icon, SynthContext synthContext, + int width, int height) { + + BufferedImage buffImage = new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + Graphics g = buffImage.createGraphics(); + g.setColor(Color.RED); + g.fillRect(0, 0, width, height); + SynthGraphicsUtils.paintIcon(icon, synthContext, g, 0, 0, width, height); + g.dispose(); + + Color iconCenterColor = new Color(buffImage.getRGB(width / 2, height / 2)); + + if (!TEST_COLOR.equals(iconCenterColor)) { + throw new RuntimeException("Icon is painted incorrectly!"); + } + } + + private static boolean checkAndSetNimbusLookAndFeel() { + try { + for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + UIManager.setLookAndFeel(info.getClassName()); + return true; + } + } + return false; + } catch (Exception ignore) { + return false; + } + } + + private static class CenteredSynthIcon implements SynthIcon { + + private final SynthIcon icon; + private final int width; + private final int height; + + public CenteredSynthIcon(SynthIcon icon, int width, int height) { + this.icon = icon; + this.width = width; + this.height = height; + } + + @Override + public void paintIcon(SynthContext syntContext, Graphics g, int x, int y, + int w, int h) { + int dw = icon.getIconWidth(syntContext); + int dh = icon.getIconHeight(syntContext); + int dx = width - dw; + int dy = height - dh; + icon.paintIcon(syntContext, g, x + dx / 2, y + dy / 2, + dw + 2, dh + 2); + } + + @Override + public int getIconWidth(SynthContext sc) { + return width; + } + + @Override + public int getIconHeight(SynthContext sc) { + return height; + } + } +} From 267d7007df618762a618a65c3a6c363c8c99bf41 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Thu, 19 Nov 2015 01:52:52 +0300 Subject: [PATCH 041/199] 8143256: The build is broken after JDK-8081411 Reviewed-by: omajid --- .../classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java | 5 ++--- .../share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java index f83112c6537..f19ac21e14d 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java +++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -150,8 +150,7 @@ class GTKIconFactory { } } - private static class DelegatingIcon extends SynthIcon implements - UIResource { + private static class DelegatingIcon implements UIResource, SynthIcon { private static final Class[] PARAM_TYPES = new Class[] { SynthContext.class, Graphics.class, int.class, int.class, int.class, int.class, int.class diff --git a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java index 160540aad78..4fa44c758e5 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java +++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -992,7 +992,7 @@ class GTKStyle extends SynthStyle implements GTKConstants { /** * An Icon that is fetched using getStockIcon. */ - private static class GTKStockIcon extends SynthIcon { + private static class GTKStockIcon implements SynthIcon { private String key; private int size; private boolean loadedLTR; From 90cb525183f67ded81f8bab489eebc2e833b2477 Mon Sep 17 00:00:00 2001 From: Omair Majid Date: Thu, 19 Nov 2015 12:52:41 -0500 Subject: [PATCH 042/199] 8142898: Prefer isFile()/isDirectory() over exists() in SoftSynthesizer Reviewed-by: serb --- .../com/sun/media/sound/SoftSynthesizer.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java index 3b12b811fb7..ef2182d6c9e 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java @@ -638,7 +638,7 @@ public final class SoftSynthesizer implements AudioSynthesizer, File javahome = new File(System.getProperties() .getProperty("java.home")); File libaudio = new File(new File(javahome, "lib"), "audio"); - if (libaudio.exists()) { + if (libaudio.isDirectory()) { File foundfile = null; File[] files = libaudio.listFiles(); if (files != null) { @@ -686,9 +686,9 @@ public final class SoftSynthesizer implements AudioSynthesizer, * Look for a default.sf2 */ for (File systemSoundFontDir : systemSoundFontsDir) { - if (systemSoundFontDir.exists()) { + if (systemSoundFontDir.isDirectory()) { File defaultSoundFont = new File(systemSoundFontDir, "default.sf2"); - if (defaultSoundFont.exists()) { + if (defaultSoundFont.isFile()) { try { return new FileInputStream(defaultSoundFont); } catch (IOException e) { @@ -708,7 +708,7 @@ public final class SoftSynthesizer implements AudioSynthesizer, .startsWith("Windows")) { File gm_dls = new File(System.getenv("SystemRoot") + "\\system32\\drivers\\gm.dls"); - if (gm_dls.exists()) { + if (gm_dls.isFile()) { try { return new FileInputStream(gm_dls); } catch (IOException e) { @@ -728,7 +728,7 @@ public final class SoftSynthesizer implements AudioSynthesizer, ".gervill"); File emg_soundbank_file = new File(userhome, "soundbank-emg.sf2"); - if (emg_soundbank_file.exists()) { + if (emg_soundbank_file.isFile()) { try { return new FileInputStream(emg_soundbank_file); } catch (IOException e) { @@ -773,12 +773,14 @@ public final class SoftSynthesizer implements AudioSynthesizer, try { File userhome = new File(System .getProperty("user.home"), ".gervill"); - if (!userhome.exists()) { - userhome.mkdirs(); + if (!userhome.isDirectory()) { + if (!userhome.mkdirs()) { + return null; + } } File emg_soundbank_file = new File( userhome, "soundbank-emg.sf2"); - if (emg_soundbank_file.exists()) { + if (emg_soundbank_file.isFile()) { return null; } return new FileOutputStream(emg_soundbank_file); From cf63d3026e3af5637f898c09dbb7bac91f9fc580 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Fri, 20 Nov 2015 13:02:06 +0100 Subject: [PATCH 043/199] 8043138: Attach API should not require jvmstat rmi protocol Reviewed-by: alanb, mchung, erikj, ihse --- make/Images.gmk | 4 ++-- modules.xml | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/make/Images.gmk b/make/Images.gmk index 04d526f2be6..6bd48bf2502 100644 --- a/make/Images.gmk +++ b/make/Images.gmk @@ -44,8 +44,8 @@ MAIN_MODULES += java.se java.smartcardio jdk.httpserver jdk.sctp \ jdk.vm.ci jdk.management # providers -PROVIDER_MODULES += jdk.charsets jdk.crypto.ec jdk.crypto.pkcs11 jdk.jvmstat jdk.localedata \ - jdk.naming.dns jdk.naming.rmi jdk.zipfs +PROVIDER_MODULES += jdk.charsets jdk.crypto.ec jdk.crypto.pkcs11 jdk.jvmstat jdk.jvmstat.rmi \ + jdk.localedata jdk.naming.dns jdk.naming.rmi jdk.zipfs # tools TOOLS_MODULES += jdk.attach jdk.compiler jdk.dev \ diff --git a/modules.xml b/modules.xml index 68a9de25848..737cbd28fd8 100644 --- a/modules.xml +++ b/modules.xml @@ -1810,16 +1810,31 @@ jdk.jvmstat java.base - java.rmi sun.jvmstat.monitor jdk.attach jdk.jcmd jdk.jconsole + jdk.jvmstat.rmi sun.jvmstat.monitor.event jdk.jcmd + jdk.jvmstat.rmi + + + sun.jvmstat.perfdata.monitor + jdk.jvmstat.rmi + + + + jdk.jvmstat.rmi + java.base + java.rmi + jdk.jvmstat + + sun.jvmstat.monitor.remote + java.rmi From dda3e3761d56d9d205fe55c37c476889ba898e67 Mon Sep 17 00:00:00 2001 From: Rajeev Chamyal Date: Fri, 20 Nov 2015 16:44:33 +0400 Subject: [PATCH 044/199] 8037575: JFrame on Windows doesn't animate when setting ICONIFIED state Reviewed-by: azvegint, alexsch --- .../native/libawt/windows/awt_Frame.cpp | 2 +- .../swing/JFrame/8037575/bug8037575.java | 263 ++++++++++++++++++ 2 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 jdk/test/javax/swing/JFrame/8037575/bug8037575.java diff --git a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp index 53f6a50149e..e9a4167a6e1 100644 --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp +++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp @@ -1372,7 +1372,7 @@ void AwtFrame::_SetState(void *param) } else { // zoom == iconify == FALSE wp.showCmd = focusable ? SW_RESTORE : SW_SHOWNOACTIVATE; } - + ::ShowWindow(hwnd, wp.showCmd); if (zoom && iconify) { wp.flags |= WPF_RESTORETOMAXIMIZED; } else { diff --git a/jdk/test/javax/swing/JFrame/8037575/bug8037575.java b/jdk/test/javax/swing/JFrame/8037575/bug8037575.java new file mode 100644 index 00000000000..cdb16962a18 --- /dev/null +++ b/jdk/test/javax/swing/JFrame/8037575/bug8037575.java @@ -0,0 +1,263 @@ +/* + * 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. + */ + +/* + * @test @bug 8037575 + * @summary JFrame doesn't animate when setting ICONIFIED state + * @requires (os.family == "windows") + * @run main/manual bug8037575 + */ +import java.awt.Frame; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import java.awt.*; +import java.awt.event.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.Timer; + +public class bug8037575 { + + private static boolean theTestPassed; + private static boolean testGeneratedInterrupt; + private static Thread mainThread; + private static int sleepTime = 30000; + private static int waitTime = 2000; + private final static JFrame frame = new JFrame("bug8037575"); + + private static void init() throws Exception { + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + String[] instructions + = { + "1) You see a dialog with buttons and text area.", + "2) Pressing Run button will start test. A new Frame will open automatically" + + " and minimize after 2 seconds.", + "3) Frame should minimize gradually with animation effect.", + "4) If frame disappers without animation then test " + + "failed otherwise passed.", + "5) Pressing Pass/Fail button will mark test as " + + "pass/fail and will shutdown JVM as well"}; + + Sysout.createDialogWithInstructions(instructions); + Sysout.printInstructions(instructions); + } + }); + } + + /** + * *************************************************** + * Standard Test Machinery Section DO NOT modify anything in this section -- + * it's a standard chunk of code which has all of the synchronisation + * necessary for the test harness. By keeping it the same in all tests, it + * is easier to read and understand someone else's test, as well as insuring + * that all tests behave correctly with the test harness. There is a section + * following this for test-defined classes + */ + public static void main(String args[]) throws InterruptedException { + + mainThread = Thread.currentThread(); + try { + init(); + } catch (Exception ex) { + Logger.getLogger(bug8037575.class.getName()).log(Level.SEVERE, null, ex); + } + try { + mainThread.sleep(sleepTime); + } catch (InterruptedException ex) { + Sysout.dispose(); + if (!theTestPassed && testGeneratedInterrupt) { + throw new RuntimeException("Test Failed"); + } + } + if (!testGeneratedInterrupt) { + Sysout.dispose(); + throw new RuntimeException("Test Failed"); + } + } + + public static synchronized void pass() { + theTestPassed = true; + testGeneratedInterrupt = true; + mainThread.interrupt(); + } + + public static synchronized void runTest() { + frame.setSize(800, 600); + frame.setVisible(true); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Timer timer = new Timer(waitTime, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent ae) { + frame.setExtendedState(Frame.ICONIFIED); + frame.dispose(); + Sysout.println("Test completed please press/fail button"); + } + }); + timer.setRepeats(false); + timer.start(); + } + + public static synchronized void fail() { + theTestPassed = false; + testGeneratedInterrupt = true; + mainThread.interrupt(); + } +} + +/** + * This is part of the standard test machinery. It creates a dialog (with the + * instructions), and is the interface for sending text messages to the user. To + * print the instructions, send an array of strings to Sysout.createDialog + * WithInstructions method. Put one line of instructions per array entry. To + * display a message for the tester to see, simply call Sysout.println with the + * string to be displayed. This mimics System.out.println but works within the + * test harness as well as standalone. + */ +class Sysout { + + private static TestDialog dialog; + private static JFrame frame; + + public static void createDialogWithInstructions(String[] instructions) { + frame = new JFrame(); + dialog = new TestDialog(frame, "Instructions"); + dialog.printInstructions(instructions); + dialog.setVisible(true); + println("Any messages for the tester will display here."); + } + + public static void printInstructions(String[] instructions) { + dialog.printInstructions(instructions); + } + + public static void println(String messageIn) { + dialog.displayMessage(messageIn); + } + + public static void dispose() { + Sysout.println("Shutting down the Java process.."); + frame.dispose(); + dialog.dispose(); + } +} + +/** + * This is part of the standard test machinery. It provides a place for the test + * instructions to be displayed, and a place for interactive messages to the + * user to be displayed. To have the test instructions displayed, see Sysout. To + * have a message to the user be displayed, see Sysout. Do not call anything in + * this dialog directly. + */ +class TestDialog extends JDialog { + + private TextArea instructionsText; + private TextArea messageText; + private int maxStringLength = 80; + private Panel buttonP = new Panel(); + private JButton run = new JButton("Run"); + private JButton passB = new JButton("Pass"); + private JButton failB = new JButton("Fail"); + + public TestDialog(JFrame frame, String name) { + super(frame, name); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + int scrollBoth = TextArea.SCROLLBARS_BOTH; + instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); + add("North", instructionsText); + + messageText = new TextArea("", 5, maxStringLength, scrollBoth); + add("Center", messageText); + + buttonP.add("East", run); + buttonP.add("East", passB); + buttonP.add("West", failB); + passB.setEnabled(false); + failB.setEnabled(false); + add("South", buttonP); + + run.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + bug8037575.runTest(); + passB.setEnabled(true); + failB.setEnabled(true); + } + }); + + passB.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + bug8037575.pass(); + } + }); + + failB.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + bug8037575.fail(); + } + }); + pack(); + + setVisible(true); + } + + public void printInstructions(String[] instructions) { + instructionsText.setText(""); + String printStr, remainingStr; + for (String instruction : instructions) { + remainingStr = instruction; + while (remainingStr.length() > 0) { + if (remainingStr.length() >= maxStringLength) { + int posOfSpace = remainingStr. + lastIndexOf(' ', maxStringLength - 1); + + if (posOfSpace <= 0) { + posOfSpace = maxStringLength - 1; + } + + printStr = remainingStr.substring(0, posOfSpace + 1); + remainingStr = remainingStr.substring(posOfSpace + 1); + } else { + printStr = remainingStr; + remainingStr = ""; + } + instructionsText.append(printStr + "\n"); + } + } + + } + + public void displayMessage(String messageIn) { + messageText.append(messageIn + "\n"); + } +} \ No newline at end of file From 90c71390a7e0b8682e94f1c021d14e6b2823c583 Mon Sep 17 00:00:00 2001 From: Peter Brunet Date: Fri, 20 Nov 2015 17:54:58 -0600 Subject: [PATCH 045/199] 8056925: Add jaccessinspector and jaccesswalker to the bin directory Add jaccessinspector, jaccesswalker to jdk.accessibility module; update launcher in make Reviewed-by: erikj, van, prr --- .../launcher/Launcher-jdk.accessibility.gmk | 71 + .../native/common/AccessBridgeDebug.cpp | 35 + .../windows/native/common/AccessBridgeDebug.h | 1 + .../jaccessinspector/MessageHistory.cpp | 155 ++ .../native/jaccessinspector/MessageHistory.h | 55 + .../jaccessinspector/jaccessinspector.cpp | 1716 +++++++++++++++++ .../jaccessinspector/jaccessinspector.h | 117 ++ .../jaccessinspectorResource.h | 88 + .../jaccessinspectorWindow.rc | 249 +++ .../native/jaccesswalker/jaccesswalker.cpp | 648 +++++++ .../native/jaccesswalker/jaccesswalker.h | 110 ++ .../jaccesswalker/jaccesswalkerResource.h | 46 + .../jaccesswalker/jaccesswalkerWindow.rc | 182 ++ .../AccessBridgeJavaEntryPoints.cpp | 13 +- .../windows/native/toolscommon/AccessInfo.cpp | 948 +++++++++ .../windows/native/toolscommon/AccessInfo.h | 56 + 16 files changed, 4486 insertions(+), 4 deletions(-) create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccessinspector/MessageHistory.cpp create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccessinspector/MessageHistory.h create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.h create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspectorResource.h create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspectorWindow.rc create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.h create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalkerResource.h create mode 100644 jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalkerWindow.rc create mode 100644 jdk/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.cpp create mode 100644 jdk/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.h diff --git a/jdk/make/launcher/Launcher-jdk.accessibility.gmk b/jdk/make/launcher/Launcher-jdk.accessibility.gmk index 33fbfc27a9c..0fa486377f1 100644 --- a/jdk/make/launcher/Launcher-jdk.accessibility.gmk +++ b/jdk/make/launcher/Launcher-jdk.accessibility.gmk @@ -56,6 +56,77 @@ ifeq ($(OPENJDK_TARGET_OS), windows) )) TARGETS += $(BUILD_JABSWITCH) + +################################################################################ +# jaccessinspector + + TOPDIR := $(JDK_TOPDIR)/src/jdk.accessibility/windows/native + TOOLS_CFLAGS := $(addprefix -I, \ + $(TOPDIR)/include/bridge \ + $(TOPDIR)/common \ + $(TOPDIR)/toolscommon) + + define SetupInspector + # Parameter 1 File name suffix + # Parameter 2 ACCESSBRIDGE_ARCH_ -D suffix + + $$(eval $$(call SetupNativeCompilation, BUILD_JACCESSINSPECTOR$1, \ + SRC := $(TOPDIR)/jaccessinspector $(TOPDIR)/common \ + $(TOPDIR)/toolscommon $(TOPDIR)/include/bridge, \ + CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 /EHsc, \ + LDFLAGS := $$(LDFLAGS_JDKEXE) /STACK:655360 Advapi32.lib User32.lib, \ + OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/jdk.accessibility/jaccessinspector$1, \ + OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_cmds/jdk.accessibility, \ + PROGRAM := jaccessinspector$1, \ + DEBUG_SYMBOLS := true, \ + VERSIONINFO_RESOURCE := $(TOPDIR)/jaccessinspector/jaccessinspectorWindow.rc, \ + RC_FLAGS := $$(RC_FLAGS) \ + -D "JDK_FNAME=jaccessinspector$1.exe" \ + -D "JDK_INTERNAL_NAME=jaccessinspector$1" \ + -D "JDK_FTYPE=0x01L", \ + )) + + TARGETS += $$(BUILD_JACCESSINSPECTOR$1) + + endef + +################################################################################ +# jaccesswalker + + define SetupWalker + # Parameter 1 File name suffix + # Parameter 2 ACCESSBRIDGE_ARCH_ -D suffix + + $$(eval $$(call SetupNativeCompilation,BUILD_JACCESSWALKER$1, \ + SRC := $(TOPDIR)/jaccesswalker $(TOPDIR)/common \ + $(TOPDIR)/toolscommon $(TOPDIR)/include/bridge, \ + CFLAGS :== $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 /EHsc, \ + LDFLAGS := $$(LDFLAGS_JDKEXE) /STACK:655360 Advapi32.lib Comctl32.lib Gdi32.lib User32.lib, \ + OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/jdk.accessibility/jaccesswalker$1, \ + OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_cmds/jdk.accessibility, \ + PROGRAM := jaccesswalker$1, \ + DEBUG_SYMBOLS := true, \ + VERSIONINFO_RESOURCE := $(TOPDIR)/jaccesswalker/jaccesswalkerWindow.rc, \ + RC_FLAGS := $$(RC_FLAGS) \ + -D "JDK_FNAME=jaccesswalker$1.exe" \ + -D "JDK_INTERNAL_NAME=jaccesswalker$1" \ + -D "JDK_FTYPE=0x01L", \ + )) + + TARGETS += $$(BUILD_JACCESSWALKER$1) + + endef + + ifeq ($(OPENJDK_TARGET_CPU_BITS), 32) + $(eval $(call SetupInspector,-32,32)) + $(eval $(call SetupWalker,-32,32)) + $(eval $(call SetupInspector,,LEGACY)) + $(eval $(call SetupWalker,,LEGACY)) + else + $(eval $(call SetupInspector,,64)) + $(eval $(call SetupWalker,,64)) + endif + endif ################################################################################ diff --git a/jdk/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp b/jdk/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp index c9decea3aa4..f41125d8c08 100644 --- a/jdk/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp +++ b/jdk/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp @@ -36,6 +36,41 @@ extern "C" { #endif +/** + * print a GetLastError message + */ +char *printError(char *msg) { + LPVOID lpMsgBuf = NULL; + static char retbuf[256]; + + if (msg != NULL) { + strncpy((char *)retbuf, msg, sizeof(retbuf)); + // if msg text is >= 256 ensure buffer is null terminated + retbuf[255] = '\0'; + } + if (!FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL )) + { + PrintDebugString(" %s: FormatMessage failed", msg); + } else { + PrintDebugString(" %s: %s", msg, (char *)lpMsgBuf); + } + if (lpMsgBuf != NULL) { + strncat((char *)retbuf, ": ", sizeof(retbuf) - strlen(retbuf) - 1); + strncat((char *)retbuf, (char *)lpMsgBuf, sizeof(retbuf) - strlen(retbuf) - 1); + } + return (char *)retbuf; +} + + /** * Send debugging info to the appropriate place */ diff --git a/jdk/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.h b/jdk/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.h index b25fb79b691..1ac7426d6e0 100644 --- a/jdk/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.h +++ b/jdk/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.h @@ -49,6 +49,7 @@ extern "C" { #endif + char *printError(char *msg); void PrintDebugString(char *msg, ...); void PrintJavaDebugString(char *msg, ...); void wPrintJavaDebugString(wchar_t *msg, ...); diff --git a/jdk/src/jdk.accessibility/windows/native/jaccessinspector/MessageHistory.cpp b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/MessageHistory.cpp new file mode 100644 index 00000000000..0975d19bca1 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/MessageHistory.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#include // includes basic windows functionality +#include +#include "MessageHistory.h" + +size_t MessageHistory::sm_MaxMessages = 1000; + +void MessageHistory::AddMessage(const char * message) { + if ( ( NULL == message ) || ( 0 == message [0] ) ) { + return; + } + + if ( m_Messages.size() >= sm_MaxMessages ) { + // Remove the oldest message + m_Messages.pop_front(); + } + + m_Messages.push_back(std::string (message) ); + m_CurrentPosition = m_Messages.end(); + -- m_CurrentPosition; +} + +const char * MessageHistory::GetFirstMessage() { + if ( m_Messages.empty() ) { + return ""; + } + + m_CurrentPosition = m_Messages.begin(); + return (*m_CurrentPosition).c_str(); +} + +const char * MessageHistory::GetPreviousMessage() { + if ( m_Messages.empty() ) { + return ""; + } + + if ( m_CurrentPosition != m_Messages.begin() ) { + -- m_CurrentPosition; + } + + return (*m_CurrentPosition).c_str(); +} + +const char * MessageHistory::GetNextMessage() { + if ( m_Messages.empty() ) { + return ""; + } + + ++ m_CurrentPosition; + if ( m_CurrentPosition == m_Messages.end() ) { + -- m_CurrentPosition; + } + + return (*m_CurrentPosition).c_str(); +} + +const char * MessageHistory::GetLastMessage() +{ + if ( m_Messages.empty() ) { + return ""; + } + + m_CurrentPosition = m_Messages.end(); + -- m_CurrentPosition; + return (*m_CurrentPosition).c_str(); +} + +BOOL MessageHistory::IsFirstMessage() { + if ( m_Messages.empty() ) { + return FALSE; + } + if ( m_CurrentPosition == m_Messages.begin() ) { + return TRUE; + } + return FALSE; +} + +BOOL MessageHistory::IsLastMessage() { + if ( m_Messages.empty() ) { + return FALSE; + } + stringlist::const_iterator itTest = m_Messages.end(); + -- itTest; + if ( itTest == m_CurrentPosition ) { + return TRUE; + } + return FALSE; +} + +size_t MessageHistory::GetMessageCount() { + size_t ret_val = m_Messages.size(); + return ret_val; +} + +const char * MessageHistory::GetCurrentMessage() { + if ( m_Messages.empty() ) { + return ""; + } + + return (*m_CurrentPosition).c_str(); +} + +const char * MessageHistory::GetMessage(const size_t messageIndex) { + if ( m_Messages.empty() ) { + return ""; + } + + if ( messageIndex >= m_Messages.size() ) { + return ""; + } + + stringlist::const_iterator it = m_Messages.begin(); + std::advance(it, messageIndex); + m_CurrentPosition = it; + + return (*it).c_str(); +} + +size_t MessageHistory::GetCurrentMessageIndex() { + if ( m_Messages.empty() ) { + return 0; + } + + stringlist::const_iterator itBegin = m_Messages.begin(); + size_t ret_val = std::distance(itBegin, m_CurrentPosition); + return ret_val; +} + +void MessageHistory::clear() { + m_Messages.clear(); +} diff --git a/jdk/src/jdk.accessibility/windows/native/jaccessinspector/MessageHistory.h b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/MessageHistory.h new file mode 100644 index 00000000000..8fda329df58 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/MessageHistory.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#ifndef __MessageHistory_H__ +#define __MessageHistory_H__ + +#include +#include + +class MessageHistory +{ +public: + static size_t sm_MaxMessages; +private: + typedef std::list< std::string > stringlist; + stringlist m_Messages; + stringlist::const_iterator m_CurrentPosition; + +public: + void AddMessage(const char * message); + const char * GetFirstMessage(); + const char * GetPreviousMessage(); + const char * GetNextMessage(); + const char * GetLastMessage(); + const char * GetCurrentMessage(); + const char * GetMessage(const size_t messageIndex); + size_t GetCurrentMessageIndex(); + BOOL IsFirstMessage(); + BOOL IsLastMessage(); + size_t GetMessageCount(); + void clear(); +}; +#endif diff --git a/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp new file mode 100644 index 00000000000..7798c64e438 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp @@ -0,0 +1,1716 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * A sample Assistive Technology which queries the JavaVM to get the Java + * Accessibility information available for a Java UI object, using the Java + * Access Bridge. + */ + +#include // includes basic windows functionality +#include + +#include "jaccessinspectorResource.h" +#include "AccessBridgeCalls.h" +#include "AccessBridgeCallbacks.h" + +#include +#include +#include + +#include "jaccessinspector.h" +#include "AccessInfo.h" +#include "MessageHistory.h" + +#define TIMER_ID 1 +#define DISPLAY_INFO_MESSAGE WM_USER+1 +#define DISPLAY_HWND_INFO_MESSAGE WM_USER+2 + +HWND theDialogWindow; +HWND theGoToDialogWindow; +HINSTANCE theInstance; +BOOL theAccessBridgeLoadedFlag; + +HHOOK prevKbdHook; +HHOOK prevMouseHook; + +BOOL updateMouse; +BOOL updateF1; +BOOL updateF2; + +BOOL trackMouse; +BOOL trackMouseExited; +BOOL trackMouseClicked; +BOOL trackMousePressed; +BOOL trackMouseReleased; + +BOOL trackFocus; +BOOL trackFocusLost; +BOOL trackCaret; +BOOL trackShutdown; + +BOOL trackMenuSelected; +BOOL trackMenuDeselected; +BOOL trackMenuCanceled; + +BOOL trackPopupVisible; +BOOL trackPopupInvisible; +BOOL trackPopupCanceled; + +//BOOL trackPropertyChange; + +BOOL trackPropertyNameChange; +BOOL trackPropertyDescriptionChange; +BOOL trackPropertyStateChange; +BOOL trackPropertyValueChange; +BOOL trackPropertySelectionChange; +BOOL trackPropertyTextChange; +BOOL trackPropertyCaretChange; +BOOL trackPropertyVisibleDataChange; +BOOL trackPropertyChildChange; +BOOL trackPropertyActiveDescendentChange; +BOOL trackPropertyTableModelChange; + + +FILE *logfile = NULL; + +MessageHistory g_MessageHistory; + +/** + * WinMain + * + */ +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd) { + MSG msg; + + g_LogStringCallback = AddToMessageHistory; + theInstance = hInst; + theDialogWindow = NULL; + theGoToDialogWindow = NULL; + + updateF1 = FALSE; + updateF2 = FALSE; + updateMouse = FALSE; + + theAccessBridgeLoadedFlag = FALSE; + + ReadActiveEventOptionsFromRegistry (); + + if (InitWindow(hInst)) { + if (initializeAccessBridge() == TRUE) { + theAccessBridgeLoadedFlag = TRUE; + ApplyEventOptions(theDialogWindow); + EnableMessageNavButtons(); + HACCEL hAccel = + ::LoadAccelerators (theInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); + + while (GetMessage(&msg, NULL, 0, 0)) { + if ( FALSE == TranslateAccelerator(theDialogWindow, hAccel, &msg) ) { + if ( ( ( NULL == theDialogWindow ) || + ( FALSE == IsDialogMessage(theDialogWindow, &msg) ) ) && + ( ( NULL == theGoToDialogWindow ) || + ( FALSE == IsDialogMessage(theGoToDialogWindow, &msg) ) ) ) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + if (theAccessBridgeLoadedFlag == TRUE) { + shutdownAccessBridge(); + } + SaveActiveEventOptionsToRegistry (); + } + } + return(0); +} + +char szAppName [] = "JACCESSINSPECTORWINDOW"; + +/** + * Real(tm) MS-Windows window initialization + * + */ +BOOL InitWindow (HANDLE hInstance) { + theDialogWindow = CreateDialog((struct HINSTANCE__ *)hInstance, + szAppName, + NULL, + jaccessinspectorDialogProc); + + // If window could not be created, return "failure". + if (!theDialogWindow) + return FALSE; + + if (logfile == null) { + logfile = fopen(JACCESSINSPECTOR_LOG, "w"); // overwrite existing log file + logString(logfile, "Starting jaccessinspector.exe %s\n", getTimeAndDate()); + } + + // Make the window visible, update its client area, & return "success". + SetWindowText(theDialogWindow, "jaccessinspector"); + ShowWindow(theDialogWindow, SW_SHOWNORMAL); + UpdateWindow(theDialogWindow); + + return TRUE; +} + +/** + * Display Accessible information about the object under the mouse + */ +void displayAccessibleInfo(long vmID, AccessibleContext ac, int x, int y) { + char buffer[HUGE_BUFSIZE]; + + getAccessibleInfo(vmID, ac, x, y, buffer, (int)(sizeof(buffer))); + displayAndLog(theDialogWindow, cjaccessinspectorText, logfile, (char *)buffer); +} + +/** + * Display Java event info + */ +void displayJavaEvent(long vmID, AccessibleContext ac, char *announcement) { + char buffer[HUGE_BUFSIZE]; + char *bufOffset; + + strncpy(buffer, announcement, sizeof(buffer)); + + bufOffset = (char *)(buffer + strlen(buffer)); + getAccessibleInfo( vmID, ac, -1, -1, bufOffset, + (int)(sizeof(buffer) - strlen(buffer)) ); + displayAndLog(theDialogWindow, cjaccessinspectorText, logfile, (char *)buffer); +} + + +/** + * Display Accessible propertyChange event info + */ +void displayAccessiblePropertyChange(long vmID, AccessibleContext ac, + char *announcement) { + char buffer[HUGE_BUFSIZE]; + char *bufOffset; + + strncpy(buffer, announcement, sizeof(buffer)); + + bufOffset = (char *) (buffer + strlen(buffer)); + getAccessibleInfo( vmID, ac, -1, -1, bufOffset, + (int)(sizeof(buffer) - strlen(buffer)) ); + displayAndLog(theDialogWindow, cjaccessinspectorText, logfile, (char *)buffer); +} + + +/** + * Update display under mouse when it moves + */ +void echoMouseObject() { + long vmID; + AccessibleContext acParent; + AccessibleContext ac; + POINT p; + HWND hwnd; + RECT windowRect; + + GetCursorPos(&p); + hwnd = WindowFromPoint(p); + if (GetAccessibleContextFromHWND(hwnd, &vmID, &acParent)) { + GetWindowRect(hwnd, &windowRect); + // send the point in global coordinates; Java will handle it! + if (GetAccessibleContextAt(vmID, acParent, (jint) p.x, (jint) p.y, &ac)) { + displayAccessibleInfo(vmID, ac, p.x, p.y); // can handle null + ReleaseJavaObject(vmID, ac); + } + } +} + + +/** + * Update display under HWND the mouse is in + */ +void echoMouseHWNDObject() { + long vmID; + AccessibleContext ac; + POINT p; + HWND hwnd; + + GetCursorPos(&p); + hwnd = WindowFromPoint(p); + + if (GetAccessibleContextFromHWND(hwnd, &vmID, &ac)) { + displayAccessibleInfo(vmID, ac, 0, 0); // can handle null + ReleaseJavaObject(vmID, ac); + } +} + +/** + * Display Accessible information about the object that has focus in + * the topmost Java HWND + * + */ +void displayFocusedObject() { + HWND hWnd; + long vmID; + AccessibleContext ac; + + hWnd = GetTopWindow(NULL); + while (hWnd != NULL) { + if (IsJavaWindow(hWnd)) { + if (GetAccessibleContextWithFocus(hWnd, &vmID, &ac) == TRUE) { + displayAccessibleInfo(vmID, ac, 0, 0); + ReleaseJavaObject(vmID, ac); + } + return; + } else { + hWnd = GetNextWindow(hWnd, GW_HWNDNEXT); + } + } +} + +/* + * Handle notification of the Java application shutting down + */ +void HandleJavaShutdown(long vmID) { + char s[1024]; + wsprintf(s, "Java VM 0x%X terminated\r\n\r\n", vmID); + + displayJavaEvent(vmID, null, s); // intentially passing null AccessibleContext + displayAndLog(theDialogWindow, cjaccessinspectorText, logfile, (char *)s); +} + +/** + * Handle a FocusGained event + */ +void HandleJavaFocusGained(long vmID, FocusEvent event, AccessibleContext ac) { + + char s[1024]; + wsprintf(s, "FocusGained\r\n\r\n"); + + if (ac != (AccessibleContext) 0) { + displayJavaEvent(vmID, ac, s); + } + + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a FocusLost event + */ +void HandleJavaFocusLost(long vmID, FocusEvent event, AccessibleContext ac) { + + // NOTE: calling GetAccessibleContextWithFocus() after a FocusLost event + // would result in a null AccessibleContext being returned, since + // at that point, no object has the focus. If the topmost window + // does not belong to a JavaVM, then no component within a JavaVM + // will have the focus (and again, GetAccessibleContextWithFocus() + // will return a null AccessibleContext). You should always get + // a FocusLost event when a window not belonging to a JavaVM becomes + // topmost. + + char s[1024]; + wsprintf(s, "FocusLost\r\n\r\n"); + + if (ac != (AccessibleContext) 0) { + displayJavaEvent(vmID, ac, s); + } + /* + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + */ + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a CaretUpdate event + */ +void HandleJavaCaretUpdate(long vmID, CaretEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a MouseClicked event + */ +void HandleMouseClicked(long vmID, MouseEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a MouseEntered event + */ +void HandleMouseEntered(long vmID, MouseEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a MouseExited event + */ +void HandleMouseExited(long vmID, MouseEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a MousePressed event + */ +void HandleMousePressed(long vmID, MouseEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a MouseReleased event + */ +void HandleMouseReleased(long vmID, MouseEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a MenuCanceled event + */ +void HandleMenuCanceled(long vmID, MenuEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a MenuDeselected event + */ +void HandleMenuDeselected(long vmID, MenuEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a MenuSelected event + */ +void HandleMenuSelected(long vmID, MenuEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a PopupMenuCanceled event + */ +void HandlePopupMenuCanceled(long vmID, MenuEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a PopupMenuWillBecomeInvisible event + */ +void HandlePopupMenuWillBecomeInvisible(long vmID, MenuEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a PopupMenuWillBecomeVisible event + */ +void HandlePopupMenuWillBecomeVisible(long vmID, MenuEvent event, AccessibleContext ac) { + if (ac != (AccessibleContext) 0) { + displayAccessibleInfo(vmID, ac, 0, 0); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + + + + +/** + * Handle a HandlePropertyNameChange event + */ +void HandlePropertyNameChange(long vmID, PropertyChangeEvent event, AccessibleContext ac, + wchar_t *oldName, wchar_t *newName) { + char s[1024]; + wsprintf(s, "Name changed event: old = %ls; new = %ls\r\n\r\n", oldName, newName); + + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, ac, s); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertyDescriptionChange event + */ +void HandlePropertyDescriptionChange( long vmID, + PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldDescription, + wchar_t *newDescription ) { + char s[1024]; + wsprintf( s, "Description changed event: old = %ls; new = %ls\r\n\r\n", + oldDescription, newDescription ); + + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, ac, s); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertyStateChange event + */ +void HandlePropertyStateChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldState, wchar_t *newState ) { + char s[1024]; + wsprintf( s, "State changed event: old = %ls; new = %ls\r\n\r\n", + oldState, newState ); + + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, ac, s); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertyValueChange event + */ +void HandlePropertyValueChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldValue, wchar_t *newValue ) { + char s[1024]; + wsprintf( s, "Value changed event: old = %ls; new = %ls\r\n\r\n", + oldValue, newValue ); + + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, ac, s); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertySelectionChange event + */ +void HandlePropertySelectionChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac ) { + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange( vmID, ac, + "Selection changed event\r\n\r\n" ); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertyTextChange event + */ +void HandlePropertyTextChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac ) { + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, ac, "Text changed event\r\n\r\n"); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertyCaretChange event + */ +void HandlePropertyCaretChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + int oldPosition, int newPosition ) { + char s[1024]; + + wsprintf( s, "Caret changed event: oldPosition = %d; newPosition = %d\r\n\r\n", + oldPosition, newPosition ); + + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, ac, s); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertyVisibleDataChange event + */ +void HandlePropertyVisibleDataChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac ) { + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange( vmID, ac, + "VisibleData changed event\r\n\r\n" ); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertyChildChange event + */ +void HandlePropertyChildChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + JOBJECT64 oldChild, JOBJECT64 newChild ) { + char buffer[HUGE_BUFSIZE]; + char *bufOffset; + + sprintf( buffer, + "Child property changed event:\r\n=======================\r\n\r\n" ); + + if (oldChild != 0) { + strncat(buffer, "Old Accessible Child info:\r\n\r\n", sizeof(buffer)); + bufOffset = (char *) (buffer + strlen(buffer)); + getAccessibleInfo( vmID, oldChild, 0, 0, bufOffset, + (int)(sizeof(buffer) - strlen(buffer)) ); + strncat(buffer, "\r\n\r\n", sizeof(buffer)); + } + + if (newChild != 0) { + strncat(buffer, "New Accessible Child info:\r\n\r\n", sizeof(buffer)); + bufOffset = (char *) (buffer + strlen(buffer)); + getAccessibleInfo( vmID, newChild, 0, 0, bufOffset, + (int)(sizeof(buffer) - strlen(buffer)) ); + strncat(buffer, "\r\n\r\n", sizeof(buffer)); + } + + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, ac, buffer); + } + + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, oldChild); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, newChild); // must always release, to stave off memory leaks +} + +/** + * Handle a HandlePropertyActiveDescendentChange event + */ +void HandlePropertyActiveDescendentChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + JOBJECT64 oldActiveDescendent, + JOBJECT64 newActiveDescendent ) { + char buffer[HUGE_BUFSIZE]; + + sprintf( buffer, + "ActiveDescendent property changed event:\r\n=======================\r\n\r\n" ); + +#ifdef _notdef + char *bufOffset; + if (oldActiveDescendent != 0) { + strncat(buffer, "Old Accessible ActiveDescendent info:\r\n\r\n", sizeof(buffer)); + bufOffset = (char *) (buffer + strlen(buffer)); + getAccessibleInfo( vmID, oldActiveDescendent, 0, 0, bufOffset, + (int)(sizeof(buffer) - strlen(buffer)) ); + strncat(buffer, "\r\n\r\n", sizeof(buffer)); + } + + if (newActiveDescendent != 0) { + strncat( buffer, "New Accessible ActiveDescendent info:\r\n\r\n", + sizeof(buffer) ); + bufOffset = (char *) (buffer + strlen(buffer)); + getAccessibleInfo( vmID, newActiveDescendent, 0, 0, bufOffset, + (int)(sizeof(buffer) - strlen(buffer)) ); + strncat(buffer, "\r\n\r\n", sizeof(buffer)); + } +#endif _notdef + + if (newActiveDescendent != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, newActiveDescendent, buffer); + } + + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, oldActiveDescendent); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, newActiveDescendent); // must always release, to stave off memory leaks +} + + +/** + * Handle a HandlePropertyTableModelChange event + */ +void HandlePropertyTableModelChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldValue, wchar_t *newValue ) { + + char s[1024]; + wsprintf( s, "Table Model Change: old = %ls; new = %ls\r\n\r\n", + oldValue, newValue ); + + if (ac != (AccessibleContext) 0) { + displayAccessiblePropertyChange(vmID, ac, s); + } + ReleaseJavaObject(vmID, ac); // must always release, to stave off memory leaks + ReleaseJavaObject(vmID, event); // must always release, to stave off memory leaks +} + + + +#define DOWN_UP_FLAG 1<<31 + +void CALLBACK TimerProc(HWND hWnd, UINT uTimerMsg, UINT uTimerID, DWORD dwTime) { + // when mouse settles from movement + KillTimer(hWnd, uTimerID); + if (updateMouse == TRUE) { + PostMessage(theDialogWindow, DISPLAY_INFO_MESSAGE, 0, 0); + } +} + +LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) { + // on mouse-up of F1 + if (code < 0) { + CallNextHookEx(prevKbdHook, code, wParam, lParam); + } else if (wParam == VK_F1 && lParam & DOWN_UP_FLAG && updateF1) { + PostMessage(theDialogWindow, DISPLAY_INFO_MESSAGE, wParam, lParam); + } else if (wParam == VK_F2 && lParam & DOWN_UP_FLAG && updateF2) { + PostMessage(theDialogWindow, DISPLAY_HWND_INFO_MESSAGE, wParam, lParam); + } + return 0; +} + +LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) { + // when mouse settles from movement + if (code < 0) { + CallNextHookEx(prevMouseHook, code, wParam, lParam); + } else { + // reset the timer every time the mouse moves + KillTimer(theDialogWindow, TIMER_ID); + SetTimer(theDialogWindow, TIMER_ID, 1000, (TIMERPROC)TimerProc); + } + return 0; +} + +void exitjaccessinspector(HWND hWnd) { + EndDialog(hWnd, TRUE); + PostQuitMessage (0); +} + +#define INSTALL_EVENT_LISTENER(toggleVal, setFP, handler) \ + if (toggleVal) { \ + setFP(handler); \ + } + +void reinstallEventListeners() { + INSTALL_EVENT_LISTENER(trackMouse, SetMouseEntered, HandleMouseEntered); + INSTALL_EVENT_LISTENER(trackMouseExited, SetMouseExited, HandleMouseExited); + INSTALL_EVENT_LISTENER(trackMouseClicked, SetMouseClicked, HandleMouseClicked); + INSTALL_EVENT_LISTENER(trackMousePressed, SetMousePressed, HandleMousePressed); + INSTALL_EVENT_LISTENER(trackMouseReleased, SetMouseReleased, HandleMouseReleased); + INSTALL_EVENT_LISTENER(trackShutdown, SetJavaShutdown, HandleJavaShutdown); + INSTALL_EVENT_LISTENER(trackFocus, SetFocusGained, HandleJavaFocusGained); + INSTALL_EVENT_LISTENER(trackFocusLost, SetFocusLost, HandleJavaFocusLost); + INSTALL_EVENT_LISTENER(trackCaret, SetCaretUpdate, HandleJavaCaretUpdate); + + INSTALL_EVENT_LISTENER(trackMenuSelected, SetMenuSelected, HandleMenuSelected); + INSTALL_EVENT_LISTENER(trackMenuDeselected, SetMenuDeselected, HandleMenuDeselected); + INSTALL_EVENT_LISTENER(trackMenuCanceled, SetMenuCanceled, HandleMenuCanceled); + + INSTALL_EVENT_LISTENER( trackPopupVisible, SetPopupMenuWillBecomeVisible, + HandlePopupMenuWillBecomeVisible ); + INSTALL_EVENT_LISTENER( trackPopupInvisible, SetPopupMenuWillBecomeInvisible, + HandlePopupMenuWillBecomeInvisible ); + INSTALL_EVENT_LISTENER( trackPopupCanceled, SetPopupMenuCanceled, + HandlePopupMenuCanceled ); + + INSTALL_EVENT_LISTENER( trackPropertyNameChange, SetPropertyNameChange, + HandlePropertyNameChange); + INSTALL_EVENT_LISTENER( trackPropertyDescriptionChange, + SetPropertyDescriptionChange, + HandlePropertyDescriptionChange ); + INSTALL_EVENT_LISTENER( trackPropertyStateChange, SetPropertyStateChange, + HandlePropertyStateChange ); + INSTALL_EVENT_LISTENER( trackPropertyValueChange, SetPropertyValueChange, + HandlePropertyValueChange ); + INSTALL_EVENT_LISTENER( trackPropertySelectionChange, + SetPropertySelectionChange, + HandlePropertySelectionChange ); + INSTALL_EVENT_LISTENER( trackPropertyTextChange, SetPropertyTextChange, + HandlePropertyTextChange ); + INSTALL_EVENT_LISTENER( trackPropertyCaretChange, SetPropertyCaretChange, + HandlePropertyCaretChange ); + INSTALL_EVENT_LISTENER( trackPropertyVisibleDataChange, + SetPropertyVisibleDataChange, + HandlePropertyVisibleDataChange ); + INSTALL_EVENT_LISTENER( trackPropertyChildChange, SetPropertyChildChange, + HandlePropertyChildChange ); + INSTALL_EVENT_LISTENER( trackPropertyActiveDescendentChange, + SetPropertyActiveDescendentChange, + HandlePropertyActiveDescendentChange ); + INSTALL_EVENT_LISTENER( trackPropertyTableModelChange, + SetPropertyTableModelChange, + HandlePropertyTableModelChange ); +} + + +#define TRACK_EVENT_TOGGLE(menuItem, toggleVal, setFP, handler) \ + case menuItem: \ + menu = GetMenu(hWnd); \ + if (toggleVal) { \ + toggleVal = FALSE; \ + CheckMenuItem(menu, menuItem, \ + MF_BYCOMMAND | MF_UNCHECKED); \ + setFP(NULL); \ + } else { \ + toggleVal = TRUE; \ + CheckMenuItem(menu, menuItem, \ + MF_BYCOMMAND | MF_CHECKED); \ + setFP(handler); \ + } \ + MaybeCheckMonitorTheSameEventsAsJAWS(menu); \ + MaybeCheckMonitorAllEvents(menu); \ + return TRUE + +INT_PTR CALLBACK jaccessinspectorDialogProc( HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam ) { + const int minWindowWidth = 540; + const int minWindowHeight = 300; + static int titleBarHeight = ::GetSystemMetrics(SM_CYSIZE); + static int menuBarHeight = ::GetSystemMetrics(SM_CYMENU); + static int borderHeight = ::GetSystemMetrics(SM_CYBORDER); + static int borderWidth = ::GetSystemMetrics(SM_CXBORDER); + static int verticalScrollBarWidth = ::GetSystemMetrics(SM_CXVSCROLL); + int command; + short width, height; + HWND dlgItem; + RECT dlgItemRect; + RECT dialogBoxRect; + LONG lT; + HMENU menu; + DWORD lastError = 0; + + switch (message) { + + case WM_CREATE: + return 0; + + case WM_INITDIALOG: + CheckMenuItem(GetMenu(hWnd), cAccessBridgeDLLLoaded, MF_BYCOMMAND | MF_CHECKED); + return TRUE; + + case WM_CLOSE: + exitjaccessinspector(hWnd); + return TRUE; + + case WM_SIZE: + width = LOWORD(lParam); + height = HIWORD(lParam); + dlgItem = GetDlgItem(theDialogWindow, cjaccessinspectorText); + ::GetWindowRect(dlgItem, &dlgItemRect); + ::GetWindowRect(theDialogWindow, &dialogBoxRect); + lT = dlgItemRect.top - dialogBoxRect.top - titleBarHeight - + menuBarHeight + (borderHeight * 4); + SetWindowPos( dlgItem, NULL, 0, 0, + width - (borderWidth * 2) - verticalScrollBarWidth, + height - lT, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + return FALSE; // let windows finish handling this + + case WM_GETMINMAXINFO: + { + LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; + lpMMI->ptMinTrackSize.x = minWindowWidth; + lpMMI->ptMinTrackSize.y = minWindowHeight; + return TRUE; + } + break; + + case WM_COMMAND: + command = LOWORD(wParam); + + switch(command) { + case cAccessBridgeDLLLoaded: // toggle; unload or load AccessBridge + if (theAccessBridgeLoadedFlag) { + shutdownAccessBridge(); + theAccessBridgeLoadedFlag = FALSE; + CheckMenuItem( GetMenu(hWnd), cAccessBridgeDLLLoaded, + MF_BYCOMMAND | MF_UNCHECKED ); + } else { + theAccessBridgeLoadedFlag = initializeAccessBridge(); + if (theAccessBridgeLoadedFlag) { + CheckMenuItem( GetMenu(hWnd), cAccessBridgeDLLLoaded, + MF_BYCOMMAND | MF_CHECKED ); + reinstallEventListeners(); + } + } + return TRUE; + + case cExitMenuItem: + exitjaccessinspector(hWnd); + return TRUE; + + TRACK_EVENT_TOGGLE( cTrackMouseMenuItem, trackMouse, SetMouseEntered, + HandleMouseEntered ); + TRACK_EVENT_TOGGLE( cTrackMouseExitedMenuItem, trackMouseExited, + SetMouseExited, HandleMouseExited ); + TRACK_EVENT_TOGGLE( cTrackMouseClickedMenuItem, trackMouseClicked, + SetMouseClicked, HandleMouseClicked ); + TRACK_EVENT_TOGGLE( cTrackMousePressedMenuItem, trackMousePressed, + SetMousePressed, HandleMousePressed ); + TRACK_EVENT_TOGGLE( cTrackMouseReleasedMenuItem, trackMouseReleased, + SetMouseReleased, HandleMouseReleased ); + TRACK_EVENT_TOGGLE( cTrackShutdownMenuItem, trackShutdown, + SetJavaShutdown, HandleJavaShutdown ); + TRACK_EVENT_TOGGLE( cTrackFocusMenuItem, trackFocus, SetFocusGained, + HandleJavaFocusGained ); + TRACK_EVENT_TOGGLE( cTrackFocusLostMenuItem, trackFocusLost, + SetFocusLost, HandleJavaFocusLost ); + TRACK_EVENT_TOGGLE( cTrackCaretMenuItem, trackCaret, SetCaretUpdate, + HandleJavaCaretUpdate ); + + TRACK_EVENT_TOGGLE( cTrackMenuSelectedMenuItem, trackMenuSelected, + SetMenuSelected, HandleMenuSelected ); + TRACK_EVENT_TOGGLE( cTrackMenuDeselectedMenuItem, trackMenuDeselected, + SetMenuDeselected, HandleMenuDeselected ); + TRACK_EVENT_TOGGLE( cTrackMenuCanceledItem, trackMenuCanceled, + SetMenuCanceled, HandleMenuCanceled ); + + TRACK_EVENT_TOGGLE( cTrackPopupBecomeVisibleMenuItem, trackPopupVisible, + SetPopupMenuWillBecomeVisible, + HandlePopupMenuWillBecomeVisible ); + TRACK_EVENT_TOGGLE( cTrackPopupBecomeInvisibleMenuItem, + trackPopupInvisible, + SetPopupMenuWillBecomeInvisible, + HandlePopupMenuWillBecomeInvisible ); + TRACK_EVENT_TOGGLE( cTrackPopupCanceledItem, trackPopupCanceled, + SetPopupMenuCanceled, HandlePopupMenuCanceled ); + + TRACK_EVENT_TOGGLE( cTrackPropertyNameItem, trackPropertyNameChange, + SetPropertyNameChange, HandlePropertyNameChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyDescriptionItem, + trackPropertyDescriptionChange, + SetPropertyDescriptionChange, + HandlePropertyDescriptionChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyStateItem, trackPropertyStateChange, + SetPropertyStateChange, HandlePropertyStateChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyValueItem, trackPropertyValueChange, + SetPropertyValueChange, HandlePropertyValueChange ); + TRACK_EVENT_TOGGLE( cTrackPropertySelectionItem, + trackPropertySelectionChange, + SetPropertySelectionChange, + HandlePropertySelectionChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyTextItem, trackPropertyTextChange, + SetPropertyTextChange, HandlePropertyTextChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyCaretItem, trackPropertyCaretChange, + SetPropertyCaretChange, HandlePropertyCaretChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyVisibleDataItem, + trackPropertyVisibleDataChange, + SetPropertyVisibleDataChange, + HandlePropertyVisibleDataChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyChildItem, trackPropertyChildChange, + SetPropertyChildChange, HandlePropertyChildChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyActiveDescendentItem, + trackPropertyActiveDescendentChange, + SetPropertyActiveDescendentChange, + HandlePropertyActiveDescendentChange ); + TRACK_EVENT_TOGGLE( cTrackPropertyTableModelChangeItem, + trackPropertyTableModelChange, + SetPropertyTableModelChange, + HandlePropertyTableModelChange ); + + case cUpdateFromMouseMenuItem: + menu = GetMenu(hWnd); + if (updateMouse) { + updateMouse = FALSE; + CheckMenuItem( menu, cUpdateFromMouseMenuItem, + MF_BYCOMMAND | MF_UNCHECKED ); + UnhookWindowsHookEx((HHOOK)MouseProc); + KillTimer(hWnd, TIMER_ID); + } else { + updateMouse = TRUE; + CheckMenuItem( menu, cUpdateFromMouseMenuItem, + MF_BYCOMMAND | MF_CHECKED ); + prevMouseHook = SetWindowsHookEx( WH_MOUSE, MouseProc, + theInstance, + ::GetCurrentThreadId() ); + if (! prevMouseHook) { + lastError = ::GetLastError(); + } + } + return TRUE; + + case cUpdateWithF1Item: + menu = GetMenu(hWnd); + if (updateF1) { + updateF1 = FALSE; + CheckMenuItem( menu, cUpdateWithF1Item, + MF_BYCOMMAND | MFS_UNCHECKED ); + UnhookWindowsHookEx((HHOOK)KeyboardProc); + } else { + updateF1 = TRUE; + CheckMenuItem( menu, cUpdateWithF1Item, + MF_BYCOMMAND | MFS_CHECKED ); + prevKbdHook = SetWindowsHookEx( WH_KEYBOARD, KeyboardProc, + theInstance, + ::GetCurrentThreadId() ); + if (! prevKbdHook) { + lastError = ::GetLastError(); + } + } + return TRUE; + + case cUpdateWithF2Item: + menu = GetMenu(hWnd); + if (updateF2) { + updateF2 = FALSE; + CheckMenuItem( menu, cUpdateWithF2Item, + MF_BYCOMMAND | MFS_UNCHECKED ); + UnhookWindowsHookEx((HHOOK)KeyboardProc); + } else { + updateF2 = TRUE; + CheckMenuItem(menu, cUpdateWithF2Item, + MF_BYCOMMAND | MFS_CHECKED); + prevKbdHook = SetWindowsHookEx( WH_KEYBOARD, KeyboardProc, + theInstance, ::GetCurrentThreadId() ); + if (! prevKbdHook) { + lastError = ::GetLastError(); + } + } + return TRUE; + + case cMonitorTheSameEventsAsJAWS: + /* + Causes jaccessinspetor to monitor the same events as JAWS. Useful + when testing to determine if a bug is specific to JAWS or if it can + be reproduced in jaccessinspector as well. + */ + trackMouse = FALSE; + trackMouseExited = FALSE; + trackMouseClicked = FALSE; + trackMousePressed = FALSE; + trackMouseReleased = FALSE; + trackFocus = TRUE; + trackFocusLost = TRUE; + trackCaret = FALSE; + trackShutdown = FALSE; + + trackMenuSelected = FALSE; + trackMenuDeselected = FALSE; + trackMenuCanceled = FALSE; + + trackPopupVisible = FALSE; + trackPopupInvisible = FALSE; + trackPopupCanceled = FALSE; + + trackPropertyNameChange = TRUE; + trackPropertyDescriptionChange = TRUE; + trackPropertyStateChange = TRUE; + trackPropertyValueChange = TRUE; + trackPropertySelectionChange = TRUE; + trackPropertyTextChange = TRUE; + trackPropertyCaretChange = TRUE; + trackPropertyVisibleDataChange = FALSE; + trackPropertyChildChange = TRUE; + trackPropertyActiveDescendentChange = TRUE; + trackPropertyTableModelChange = FALSE; + + ApplyEventOptions(hWnd); + + return TRUE; + + case cMonitorAllEvents: + /* + Causes jaccessinspector to monitor all Java Events and all + Accessibility Events. + */ + trackMouse = TRUE; + trackMouseExited = TRUE; + trackMouseClicked = TRUE; + trackMousePressed = TRUE; + trackMouseReleased = TRUE; + trackFocus = TRUE; + trackFocusLost = TRUE; + trackCaret = TRUE; + trackShutdown = TRUE; + + trackMenuSelected = TRUE; + trackMenuDeselected = TRUE; + trackMenuCanceled = TRUE; + + trackPopupVisible = TRUE; + trackPopupInvisible = TRUE; + trackPopupCanceled = TRUE; + + trackPropertyNameChange = TRUE; + trackPropertyDescriptionChange = TRUE; + trackPropertyStateChange = TRUE; + trackPropertyValueChange = TRUE; + trackPropertySelectionChange = TRUE; + trackPropertyTextChange = TRUE; + trackPropertyCaretChange = TRUE; + trackPropertyVisibleDataChange = TRUE; + trackPropertyChildChange = TRUE; + trackPropertyActiveDescendentChange = TRUE; + trackPropertyTableModelChange = TRUE; + + ApplyEventOptions(hWnd); + + return TRUE; + + case cFirstMessage: + { + const char * messageText = g_MessageHistory.GetFirstMessage (); + if ( ( NULL != messageText ) && ( 0 != messageText [0] ) ) { + ::SetDlgItemText( theDialogWindow, cjaccessinspectorText, + messageText ); + } + EnableMessageNavButtons(); + UpdateMessageNumber(); + return TRUE; + } + break; + + case cPreviousMessage: + { + const char * messageText = g_MessageHistory.GetPreviousMessage (); + if ( ( NULL != messageText ) && ( 0 != messageText [0] ) ) { + ::SetDlgItemText( theDialogWindow, cjaccessinspectorText, + messageText ); + } + EnableMessageNavButtons(); + UpdateMessageNumber(); + return TRUE; + } + break; + + case cNextMessage: + { + const char * messageText = g_MessageHistory.GetNextMessage (); + if ( ( NULL != messageText ) && ( 0 != messageText [0] ) ) { + ::SetDlgItemText( theDialogWindow, cjaccessinspectorText, + messageText ); + } + EnableMessageNavButtons(); + UpdateMessageNumber(); + return TRUE; + } + break; + + case cLastMessage: + { + const char * messageText = g_MessageHistory.GetLastMessage(); + if ( ( NULL != messageText ) && ( 0 != messageText [0] ) ) { + ::SetDlgItemText( theDialogWindow, cjaccessinspectorText, + messageText ); + } + EnableMessageNavButtons(); + UpdateMessageNumber(); + return TRUE; + } + break; + + case cResetAllEvents: + trackMouse = FALSE; + trackMouseExited = FALSE; + trackMouseClicked = FALSE; + trackMousePressed = FALSE; + trackMouseReleased = FALSE; + trackFocus = FALSE; + trackFocusLost = FALSE; + trackCaret = FALSE; + trackShutdown = FALSE; + + trackMenuSelected = FALSE; + trackMenuDeselected = FALSE; + trackMenuCanceled = FALSE; + + trackPopupVisible = FALSE; + trackPopupInvisible = FALSE; + trackPopupCanceled = FALSE; + + trackPropertyNameChange = FALSE; + trackPropertyDescriptionChange = FALSE; + trackPropertyStateChange = FALSE; + trackPropertyValueChange = FALSE; + trackPropertySelectionChange = FALSE; + trackPropertyTextChange = FALSE; + trackPropertyCaretChange = FALSE; + trackPropertyVisibleDataChange = FALSE; + trackPropertyChildChange = FALSE; + trackPropertyActiveDescendentChange = FALSE; + trackPropertyTableModelChange = FALSE; + + ApplyEventOptions(hWnd); + + return TRUE; + + case cGoToMessage: + InitGoToMessageDialogBox(theInstance); + break; + + case cClearMessageHistory: + g_MessageHistory.clear(); + ::SetDlgItemText(theDialogWindow, cjaccessinspectorText, NULL); + EnableMessageNavButtons(); + UpdateMessageNumber(); + break; + } + break; + + case DISPLAY_INFO_MESSAGE: + echoMouseObject(); + return TRUE; + + case DISPLAY_HWND_INFO_MESSAGE: + echoMouseHWNDObject(); + return TRUE; + } + + return FALSE; +} + +#define SaveOptionToRegistry(optionVar) { \ + SetValue = RegSetValueEx( hKey, #optionVar, 0, REG_DWORD, \ + (LPBYTE)(&optionVar), sizeof(DWORD)); \ + if ( ERROR_SUCCESS != SetValue ) { \ + ++ failureCount; \ + } \ +} + +BOOL SaveActiveEventOptionsToRegistry() { + LONG CreateKey = ERROR_SUCCESS; + HKEY hKey = NULL; + DWORD Disposition = 0; + + CreateKey = ::RegCreateKeyEx( HKEY_CURRENT_USER, + jaccessinspectorOptionsRegistryKey, 0, 0, 0, + KEY_READ|KEY_WRITE, 0, &hKey, &Disposition ); + if ( ( ERROR_SUCCESS != CreateKey ) || ( NULL == hKey ) ) { + return FALSE; + } + + LONG SetValue = ERROR_SUCCESS; + unsigned long failureCount = 0; + + SaveOptionToRegistry(trackMouse); + SaveOptionToRegistry(trackMouseExited); + SaveOptionToRegistry(trackMouseClicked); + SaveOptionToRegistry(trackMousePressed); + SaveOptionToRegistry(trackMouseReleased); + SaveOptionToRegistry(trackShutdown); + SaveOptionToRegistry(trackFocus); + SaveOptionToRegistry(trackFocusLost); + SaveOptionToRegistry(trackCaret); + SaveOptionToRegistry(trackMenuSelected); + SaveOptionToRegistry(trackMenuDeselected); + SaveOptionToRegistry(trackMenuCanceled); + SaveOptionToRegistry(trackPopupVisible); + SaveOptionToRegistry(trackPopupInvisible); + SaveOptionToRegistry(trackPopupCanceled); + SaveOptionToRegistry(trackPropertyNameChange); + SaveOptionToRegistry(trackPropertyDescriptionChange); + SaveOptionToRegistry(trackPropertyStateChange); + SaveOptionToRegistry(trackPropertyValueChange); + SaveOptionToRegistry(trackPropertySelectionChange); + SaveOptionToRegistry(trackPropertyTextChange); + SaveOptionToRegistry(trackPropertyCaretChange); + SaveOptionToRegistry(trackPropertyVisibleDataChange); + SaveOptionToRegistry(trackPropertyChildChange); + SaveOptionToRegistry(trackPropertyActiveDescendentChange); + SaveOptionToRegistry(trackPropertyTableModelChange); + + ::RegFlushKey(hKey); + ::RegCloseKey(hKey); + + if ( 0 == failureCount ) { + return TRUE; + } + return FALSE; +} + +#define ReadOptionFromRegistry(optionVar) { \ + Type = Value = 0; \ + ValueSize = sizeof(DWORD); \ + QueryValue = ::RegQueryValueEx( hKey, #optionVar, NULL, &Type, \ + (LPBYTE)(&Value), &ValueSize); \ + if ( ( ERROR_SUCCESS == QueryValue ) && ( REG_DWORD == Type ) ) { \ + optionVar = static_cast(Value); \ + } else { \ + ++ failureCount; \ + } \ +} + +BOOL ReadActiveEventOptionsFromRegistry() { + + trackMouse = FALSE; + trackMouseExited = FALSE; + trackMouseClicked = FALSE; + trackMousePressed = FALSE; + trackMouseReleased = FALSE; + + trackShutdown = FALSE; + trackFocus = FALSE; + trackFocusLost = FALSE; + trackCaret = FALSE; + trackMenuSelected = FALSE; + trackMenuDeselected = FALSE; + trackMenuCanceled = FALSE; + trackPopupVisible = FALSE; + trackPopupInvisible = FALSE; + trackPopupCanceled = FALSE; + + trackPropertyNameChange = FALSE; + trackPropertyDescriptionChange = FALSE; + trackPropertyStateChange = FALSE; + trackPropertyValueChange = FALSE; + trackPropertySelectionChange = FALSE; + trackPropertyTextChange = FALSE; + trackPropertyCaretChange = FALSE; + trackPropertyVisibleDataChange = FALSE; + trackPropertyChildChange = FALSE; + trackPropertyActiveDescendentChange = FALSE; + trackPropertyTableModelChange = FALSE; + + LONG OpenKey = ERROR_SUCCESS; + HKEY hKey = NULL; + OpenKey = ::RegOpenKeyEx( HKEY_CURRENT_USER, + jaccessinspectorOptionsRegistryKey, 0, + KEY_READ, &hKey ); + if ( ( ERROR_SUCCESS != OpenKey ) || ( NULL == hKey ) ) { + return FALSE; + } + + LONG QueryValue = ERROR_SUCCESS; + unsigned long failureCount = 0; + DWORD Type, Value, ValueSize; + + ReadOptionFromRegistry(trackMouse); + ReadOptionFromRegistry(trackMouseExited); + ReadOptionFromRegistry(trackMouseClicked); + ReadOptionFromRegistry(trackMousePressed); + ReadOptionFromRegistry(trackMouseReleased); + ReadOptionFromRegistry(trackShutdown); + ReadOptionFromRegistry(trackFocus); + ReadOptionFromRegistry(trackFocusLost); + ReadOptionFromRegistry(trackCaret); + ReadOptionFromRegistry(trackMenuSelected); + ReadOptionFromRegistry(trackMenuDeselected); + ReadOptionFromRegistry(trackMenuCanceled); + ReadOptionFromRegistry(trackPopupVisible); + ReadOptionFromRegistry(trackPopupInvisible); + ReadOptionFromRegistry(trackPopupCanceled); + ReadOptionFromRegistry(trackPropertyNameChange); + ReadOptionFromRegistry(trackPropertyDescriptionChange); + ReadOptionFromRegistry(trackPropertyStateChange); + ReadOptionFromRegistry(trackPropertyValueChange); + ReadOptionFromRegistry(trackPropertySelectionChange); + ReadOptionFromRegistry(trackPropertyTextChange); + ReadOptionFromRegistry(trackPropertyCaretChange); + ReadOptionFromRegistry(trackPropertyVisibleDataChange); + ReadOptionFromRegistry(trackPropertyChildChange); + ReadOptionFromRegistry(trackPropertyActiveDescendentChange); + ReadOptionFromRegistry(trackPropertyTableModelChange); + + ::RegCloseKey(hKey); + + if ( 0 == failureCount ) { + return TRUE; + } + return FALSE; +} + +#define APPLY_EVENT_OPTION(menuItem, optionVar, setFP, handler) \ +{ \ + if ( optionVar ) { \ + ::CheckMenuItem(menu, menuItem, MF_BYCOMMAND | MF_CHECKED); \ + setFP (handler); \ + } else { \ + ::CheckMenuItem(menu, menuItem, MF_BYCOMMAND | MF_UNCHECKED); \ + setFP (NULL); \ + } \ +} + +void ApplyEventOptions (HWND hWnd) { + + HMENU menu = ::GetMenu (hWnd); + APPLY_EVENT_OPTION( cTrackMouseMenuItem, trackMouse, SetMouseEntered, + HandleMouseEntered ); + APPLY_EVENT_OPTION( cTrackMouseExitedMenuItem, trackMouseExited, + SetMouseExited, HandleMouseExited ); + APPLY_EVENT_OPTION( cTrackMouseClickedMenuItem, trackMouseClicked, + SetMouseClicked, HandleMouseClicked ); + APPLY_EVENT_OPTION( cTrackMousePressedMenuItem, trackMousePressed, + SetMousePressed, HandleMousePressed ); + APPLY_EVENT_OPTION( cTrackMouseReleasedMenuItem, trackMouseReleased, + SetMouseReleased, HandleMouseReleased ); + + APPLY_EVENT_OPTION( cTrackShutdownMenuItem, trackShutdown, SetJavaShutdown, + HandleJavaShutdown ); + APPLY_EVENT_OPTION( cTrackFocusMenuItem, trackFocus, SetFocusGained, + HandleJavaFocusGained ); + APPLY_EVENT_OPTION( cTrackFocusLostMenuItem, trackFocusLost, SetFocusLost, + HandleJavaFocusLost ); + APPLY_EVENT_OPTION( cTrackCaretMenuItem, trackCaret, SetCaretUpdate, + HandleJavaCaretUpdate ); + + APPLY_EVENT_OPTION( cTrackMenuSelectedMenuItem, trackMenuSelected, + SetMenuSelected, HandleMenuSelected ); + APPLY_EVENT_OPTION( cTrackMenuDeselectedMenuItem, trackMenuDeselected, + SetMenuDeselected, HandleMenuDeselected ); + APPLY_EVENT_OPTION( cTrackMenuCanceledItem, trackMenuCanceled, + SetMenuCanceled, HandleMenuCanceled ); + + APPLY_EVENT_OPTION( cTrackPopupBecomeVisibleMenuItem, trackPopupVisible, + SetPopupMenuWillBecomeVisible, + HandlePopupMenuWillBecomeVisible ); + APPLY_EVENT_OPTION( cTrackPopupBecomeInvisibleMenuItem, trackPopupInvisible, + SetPopupMenuWillBecomeInvisible, + HandlePopupMenuWillBecomeInvisible ); + APPLY_EVENT_OPTION( cTrackPopupCanceledItem, trackPopupCanceled, + SetPopupMenuCanceled, HandlePopupMenuCanceled ); + + APPLY_EVENT_OPTION( cTrackPropertyNameItem, trackPropertyNameChange, + SetPropertyNameChange, HandlePropertyNameChange ); + APPLY_EVENT_OPTION( cTrackPropertyDescriptionItem, + trackPropertyDescriptionChange, + SetPropertyDescriptionChange, + HandlePropertyDescriptionChange ); + APPLY_EVENT_OPTION( cTrackPropertyStateItem, trackPropertyStateChange, + SetPropertyStateChange, HandlePropertyStateChange ); + APPLY_EVENT_OPTION( cTrackPropertyValueItem, trackPropertyValueChange, + SetPropertyValueChange, HandlePropertyValueChange ); + APPLY_EVENT_OPTION( cTrackPropertySelectionItem, + trackPropertySelectionChange, + SetPropertySelectionChange, + HandlePropertySelectionChange); + APPLY_EVENT_OPTION( cTrackPropertyTextItem, trackPropertyTextChange, + SetPropertyTextChange, HandlePropertyTextChange ); + APPLY_EVENT_OPTION( cTrackPropertyCaretItem, trackPropertyCaretChange, + SetPropertyCaretChange, HandlePropertyCaretChange ); + APPLY_EVENT_OPTION( cTrackPropertyVisibleDataItem, + trackPropertyVisibleDataChange, + SetPropertyVisibleDataChange, + HandlePropertyVisibleDataChange ); + APPLY_EVENT_OPTION( cTrackPropertyChildItem, trackPropertyChildChange, + SetPropertyChildChange, HandlePropertyChildChange ); + APPLY_EVENT_OPTION( cTrackPropertyActiveDescendentItem, + trackPropertyActiveDescendentChange, + SetPropertyActiveDescendentChange, + HandlePropertyActiveDescendentChange ); + APPLY_EVENT_OPTION( cTrackPropertyTableModelChangeItem, + trackPropertyTableModelChange, + SetPropertyTableModelChange, + HandlePropertyTableModelChange ); + + MaybeCheckMonitorTheSameEventsAsJAWS(menu); + MaybeCheckMonitorAllEvents(menu); +} + +BOOL EnableDlgItem(HWND hDlg, int nIDDlgItem, BOOL bEnable) { + HWND dlgItem = ::GetDlgItem(hDlg, nIDDlgItem); + if ( NULL == dlgItem ) { + return FALSE; + } + return ::EnableWindow (dlgItem, bEnable); +} + +void EnableMessageNavButtons() { + HWND FocusWindow = ::GetFocus(); + int FocusCtrlID = ::GetDlgCtrlID(FocusWindow); + BOOL DisabledFocusWindow = FALSE; + if ( 0 == g_MessageHistory.GetMessageCount () ) { + EnableDlgItem(theDialogWindow, cFirstMessage, FALSE); + EnableDlgItem(theDialogWindow, cPreviousMessage, FALSE); + EnableDlgItem(theDialogWindow, cMessageNumber, FALSE); + EnableDlgItem(theDialogWindow, cNextMessage, FALSE); + EnableDlgItem(theDialogWindow, cLastMessage, FALSE); + } else if ( g_MessageHistory.IsFirstMessage () ) { + EnableDlgItem(theDialogWindow, cFirstMessage, FALSE); + EnableDlgItem(theDialogWindow, cPreviousMessage, FALSE); + EnableDlgItem(theDialogWindow, cMessageNumber, TRUE); + EnableDlgItem(theDialogWindow, cNextMessage, TRUE); + EnableDlgItem(theDialogWindow, cLastMessage, TRUE); + if ( ( cFirstMessage == FocusCtrlID ) || + ( cPreviousMessage == FocusCtrlID ) ) { + DisabledFocusWindow = TRUE; + } + } else if ( g_MessageHistory.IsLastMessage () ) { + EnableDlgItem(theDialogWindow, cFirstMessage, TRUE); + EnableDlgItem(theDialogWindow, cPreviousMessage, TRUE); + EnableDlgItem(theDialogWindow, cMessageNumber, TRUE); + EnableDlgItem(theDialogWindow, cNextMessage, FALSE); + EnableDlgItem(theDialogWindow, cLastMessage, FALSE); + + if ( ( cNextMessage == FocusCtrlID ) || + ( cLastMessage == FocusCtrlID ) ) { + DisabledFocusWindow = TRUE; + } + } else { + EnableDlgItem(theDialogWindow, cFirstMessage, TRUE); + EnableDlgItem(theDialogWindow, cPreviousMessage, TRUE); + EnableDlgItem(theDialogWindow, cMessageNumber, TRUE); + EnableDlgItem(theDialogWindow, cNextMessage, TRUE); + EnableDlgItem(theDialogWindow, cLastMessage, TRUE); + } + + if ( DisabledFocusWindow ) { + /* + We just disabled the window that had the focus. Set focus to the + cjaccessinspectorText window. Otherwise it will no longer be possible + to tab through the controls in jaccessinspector. + */ + HWND jaccessinspectorText = + ::GetDlgItem(theDialogWindow, cjaccessinspectorText); + if ( jaccessinspectorText ) { + ::SetFocus(jaccessinspectorText); + } + } +} + +void WINAPI AddToMessageHistory(const char * message) { + g_MessageHistory.AddMessage(message); + EnableMessageNavButtons(); + UpdateMessageNumber(); +} + +BOOL UpdateMessageNumber () { + HWND dlgItem = ::GetDlgItem(theDialogWindow, cMessageNumber); + if ( NULL == dlgItem ) { + return FALSE; + } + + size_t messageCount = g_MessageHistory.GetMessageCount(); + size_t messageNumber = g_MessageHistory.GetCurrentMessageIndex() + 1; + char text [32] = {0}; + if ( 0 != messageCount ) { + ::_snprintf(text, sizeof(text), "%d of %d", messageNumber, messageCount); + } + return ::SetWindowText(dlgItem, text); +} + +INT_PTR CALLBACK GoToMessageDialogProc( HWND hDlg, UINT message, WPARAM wParam, + LPARAM lParam ) { + BOOL ret_val = FALSE; + switch ( message ) { + case WM_INITDIALOG: + { + /* + This code to center the Go To Message dialog box in the + jaccessinspector window was taken from + . + */ + HWND hwndOwner = NULL; + RECT rcOwner = { 0, 0, 0, 0 }; + RECT rc = { 0, 0, 0, 0 }; + RECT rcDlg = { 0, 0, 0, 0 }; + + // Get the owner window and dialog box rectangles. + if ( NULL == (hwndOwner = GetParent(hDlg)) ) { + hwndOwner = GetDesktopWindow(); + } + + GetWindowRect(hwndOwner, &rcOwner); + GetWindowRect(hDlg, &rcDlg); + CopyRect(&rc, &rcOwner); + + // Offset the owner and dialog box rectangles so that right and + // bottom values represent the width and height, and then offset + // the owner again to discard space taken up by the dialog box. + OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); + OffsetRect(&rc, -rc.left, -rc.top); + OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); + + // The new position is the sum of half the remaining space and the + // owner's original position. + SetWindowPos (hDlg, + HWND_TOP, + rcOwner.left + (rc.right / 2), + rcOwner.top + (rc.bottom / 2), + 0, 0, // Ignores size arguments. + SWP_NOSIZE); + } + break; + + case WM_COMMAND: + switch ( LOWORD (wParam) ) { + case IDOK: + { + size_t GoToMessageNumber = 0; + BOOL Translated = FALSE; + GoToMessageNumber = GetDlgItemInt( hDlg, IDC_MESSAGE_NUMBER_EDIT, + &Translated, FALSE ); + EndDialog (hDlg, IDOK); + theGoToDialogWindow = NULL; + + if ( ( Translated ) && ( GoToMessageNumber > 0 ) ) { + const char * messageText = NULL; + if ( (GoToMessageNumber - 1) < + g_MessageHistory.GetMessageCount() ) { + messageText = + g_MessageHistory.GetMessage(GoToMessageNumber - 1); + } else if ( (GoToMessageNumber - 1) >= + g_MessageHistory.GetMessageCount() ) { + messageText = g_MessageHistory.GetLastMessage(); + } + if ( ( NULL != messageText ) && ( 0 != messageText [0] ) ) { + ::SetDlgItemText( theDialogWindow, cjaccessinspectorText, + messageText ); + } + EnableMessageNavButtons(); + UpdateMessageNumber(); + } + } + break; + case IDCANCEL: + EndDialog(hDlg, IDCANCEL); + theGoToDialogWindow = NULL; + break; + } + break; + } + return ret_val; +} + +BOOL InitGoToMessageDialogBox (HANDLE hInstance) { + theGoToDialogWindow = CreateDialog ( + (struct HINSTANCE__ *)hInstance, MAKEINTRESOURCE(IDD_GO_TO_MESSAGE), + theDialogWindow, GoToMessageDialogProc); + + if ( NULL == theGoToDialogWindow ) { + return FALSE; + } + + ShowWindow (theGoToDialogWindow, SW_SHOW); + return TRUE; +} + +BOOL ShouldCheckMonitorTheSameEventsAsJAWS () { + if ( + ( FALSE == trackMouse ) + && ( FALSE == trackMouseExited ) + && ( FALSE == trackMouseClicked ) + && ( FALSE == trackMousePressed ) + && ( FALSE == trackMouseReleased ) + && ( TRUE == trackFocus ) + && ( TRUE == trackFocusLost ) + && ( FALSE == trackCaret ) + && ( FALSE == trackShutdown ) + && ( FALSE == trackMenuSelected ) + && ( FALSE == trackMenuDeselected ) + && ( FALSE == trackMenuCanceled ) + && ( FALSE == trackPopupVisible ) + && ( FALSE == trackPopupInvisible ) + && ( FALSE == trackPopupCanceled ) + && ( TRUE == trackPropertyNameChange ) + && ( TRUE == trackPropertyDescriptionChange ) + && ( TRUE == trackPropertyStateChange ) + && ( TRUE == trackPropertyValueChange ) + && ( TRUE == trackPropertySelectionChange ) + && ( TRUE == trackPropertyTextChange ) + && ( TRUE == trackPropertyCaretChange ) + && ( FALSE == trackPropertyVisibleDataChange ) + && ( TRUE == trackPropertyChildChange ) + && ( TRUE == trackPropertyActiveDescendentChange ) + && ( FALSE == trackPropertyTableModelChange ) + ) + { + return TRUE; + } + + return FALSE; +} + +void MaybeCheckMonitorTheSameEventsAsJAWS(HMENU menu) { + UINT uCheck = MF_BYCOMMAND | MF_UNCHECKED; + if ( ShouldCheckMonitorTheSameEventsAsJAWS() ) { + uCheck = MF_BYCOMMAND | MF_CHECKED; + } + ::CheckMenuItem(menu, cMonitorTheSameEventsAsJAWS, uCheck); +} + +BOOL ShouldCheckMonitorAllEvents() { + if ( + ( TRUE == trackMouse ) + && ( TRUE == trackMouseExited ) + && ( TRUE == trackMouseClicked ) + && ( TRUE == trackMousePressed ) + && ( TRUE == trackMouseReleased ) + && ( TRUE == trackFocus ) + && ( TRUE == trackFocusLost ) + && ( TRUE == trackCaret ) + && ( TRUE == trackShutdown ) + && ( TRUE == trackMenuSelected ) + && ( TRUE == trackMenuDeselected ) + && ( TRUE == trackMenuCanceled ) + && ( TRUE == trackPopupVisible ) + && ( TRUE == trackPopupInvisible ) + && ( TRUE == trackPopupCanceled ) + && ( TRUE == trackPropertyNameChange ) + && ( TRUE == trackPropertyDescriptionChange ) + && ( TRUE == trackPropertyStateChange ) + && ( TRUE == trackPropertyValueChange ) + && ( TRUE == trackPropertySelectionChange ) + && ( TRUE == trackPropertyTextChange ) + && ( TRUE == trackPropertyCaretChange ) + && ( TRUE == trackPropertyVisibleDataChange ) + && ( TRUE == trackPropertyChildChange ) + && ( TRUE == trackPropertyActiveDescendentChange ) + && ( TRUE == trackPropertyTableModelChange ) + ) + { + return TRUE; + } + + return FALSE; +} + +void MaybeCheckMonitorAllEvents(HMENU menu) { + UINT uCheck = MF_BYCOMMAND | MF_UNCHECKED; + if ( ShouldCheckMonitorAllEvents() ) { + uCheck = MF_BYCOMMAND | MF_CHECKED; + } + ::CheckMenuItem(menu, cMonitorAllEvents, uCheck); +} diff --git a/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.h b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.h new file mode 100644 index 00000000000..bbf29d51f36 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#define JACCESSINSPECTOR_LOG "jaccessinspector.log" + +void CALLBACK TimerProc(HWND hWnd, UINT uTimerMsg, UINT uTimerID, DWORD dwTime); +LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam); + +BOOL InitWindow(HANDLE hInstance); +INT_PTR CALLBACK jaccessinspectorDialogProc( HWND hDlg, UINT message, + WPARAM wParam, LPARAM lParam ); + +void HandleJavaPropertyChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *name, wchar_t *oldValue, + wchar_t *newValue ); + +void HandleJavaShutdown(long vmID); +void HandleJavaFocusGained(long vmID, FocusEvent event, AccessibleContext ac); +void HandleJavaFocusLost(long vmID, FocusEvent event, AccessibleContext ac); + +void HandleJavaCaretUpdate(long vmID, CaretEvent event, AccessibleContext ac); + +void HandleMouseClicked(long vmID, MouseEvent event, AccessibleContext ac); +void HandleMouseEntered(long vmID, MouseEvent event, AccessibleContext ac); +void HandleMouseExited(long vmID, MouseEvent event, AccessibleContext ac); +void HandleMousePressed(long vmID, MouseEvent event, AccessibleContext ac); +void HandleMouseReleased(long vmID, MouseEvent event, AccessibleContext ac); + +void HandleMenuCanceled(long vmID, MenuEvent event, AccessibleContext ac); +void HandleMenuDeselected(long vmID, MenuEvent event, AccessibleContext ac); +void HandleMenuSelected(long vmID, MenuEvent event, AccessibleContext ac); +void HandlePopupMenuCanceled(long vmID, MenuEvent event, AccessibleContext ac); +void HandlePopupMenuWillBecomeInvisible( long vmID, MenuEvent event, + AccessibleContext ac ); +void HandlePopupMenuWillBecomeVisible( long vmID, MenuEvent event, + AccessibleContext ac ); + +void HandlePropertyNameChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldName, wchar_t *newName); +void HandlePropertyDescriptionChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldDescription, + wchar_t *newDescription ); +void HandlePropertyStateChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldState, wchar_t *newState ); +void HandlePropertyValueChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldValue, wchar_t *newValue ); +void HandlePropertySelectionChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac ); +void HandlePropertyTextChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac ); +void HandlePropertyCaretChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + int oldPosition, int newPosition ); +void HandlePropertyVisibleDataChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac ); +void HandlePropertyChildChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + jobject oldChild, jobject newChild ); +void HandlePropertyActiveDescendentChange(long vmID, PropertyChangeEvent event, + AccessibleContext ac, + jobject oldActiveDescendent, + jobject newActiveDescendent); + +void HandlePropertyTableModelChange( long vmID, PropertyChangeEvent event, + AccessibleContext ac, + wchar_t *oldValue, wchar_t *newValue ); + +char *getAccessibleInfo( long vmID, AccessibleContext ac, + int x, int y, char *buffer, int bufsize ); + +char *getTimeAndDate(); +void displayAndLogText(char *buffer, ...); + +const char * const jaccessinspectorOptionsRegistryKey = + "Software\\JavaSoft\\Java Development Kit\\jaccessinspector"; +BOOL SaveActiveEventOptionsToRegistry(); +BOOL ReadActiveEventOptionsFromRegistry(); +void ApplyEventOptions(HWND hWnd); +BOOL EnableDlgItem(HWND hDlg, int nIDDlgItem, BOOL bEnable); +void EnableMessageNavButtons(); +void WINAPI AddToMessageHistory(const char * message); +BOOL UpdateMessageNumber(); +INT_PTR CALLBACK GoToMessageDialogProc( HWND hDlg, UINT message, WPARAM wParam, + LPARAM lParam ); +BOOL InitGoToMessageDialogBox(HANDLE hInstance); +BOOL ShouldCheckMonitorTheSameEventsAsJAWS(); +void MaybeCheckMonitorTheSameEventsAsJAWS(HMENU menu); +BOOL ShouldCheckMonitorAllEvents(); +void MaybeCheckMonitorAllEvents(HMENU menu); diff --git a/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspectorResource.h b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspectorResource.h new file mode 100644 index 00000000000..8155551ea31 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspectorResource.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#define IDR_ACCELERATOR 102 +#define IDD_GO_TO_MESSAGE 103 +#define cjaccessinspectorText 1001 +#define cFirstMessage 1029 +#define cPreviousMessage 1030 +#define cNextMessage 1031 +#define cLastMessage 1032 +#define cMessageNumber 1033 +#define IDC_MESSAGE_NUMBER_EDIT 1034 +#define cFerretMenus 10000 +#define cFileMenu 10100 +#define cAccessBridgeDLLLoaded 10101 +#define cExitMenuItem 10102 +#define cJavaEventsMenu 10200 +#define cTrackMouseMenuItem 10201 +#define cTrackFocusMenuItem 10203 +#define cTrackCaretMenuItem 10204 +#define cTrackMenuSelectedMenuItem 10205 +#define cTrackMenuDeselectedMenuItem 10206 +#define cTrackMenuCanceledItem 10207 +#define cTrackPopupBecomeVisibleMenuItem 10208 +#define cTrackPopupBecomeInvisibleMenuItem 10209 +#define cTrackPopupCanceledItem 10210 +#define cUpdateSettingsMenu 10300 +#define cUpdateWithF1Item 10301 +#define cUpdateWithF2Item 10302 +#define cUpdateFromMouseMenuItem 10304 +#define cAccessibilityEventsMenu 10400 +#define cTrackPropertyNameItem 10401 +#define cTrackPropertyDescriptionItem 10402 +#define cTrackPropertyStateItem 10403 +#define cTrackPropertyValueItem 10404 +#define cTrackPropertySelectionItem 10405 +#define cTrackPropertyTextItem 10406 +#define cTrackPropertyCaretItem 10407 +#define cTrackPropertyVisibleDataItem 10408 +#define cTrackPropertyChildItem 10409 +#define cTrackPropertyActiveDescendentItem 10410 +#define cTrackPropertyTableModelChangeItem 10411 +#define cTrackShutdownMenuItem 10412 +#define cMonitorTheSameEventsAsJAWS 10413 +#define cTrackFocusLostMenuItem 10414 +#define cTrackMouseExitedMenuItem 10415 +#define cTrackMouseClickedMenuItem 10416 +#define cTrackMousePressedMenuItem 10417 +#define cTrackMouseReleasedMenuItem 10418 +#define cResetAllEvents 10419 +#define cGoToMessage 10420 +#define cClearMessageHistory 10421 +#define cMonitorAllEvents 10422 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 10423 +#define _APS_NEXT_CONTROL_VALUE 1035 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspectorWindow.rc b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspectorWindow.rc new file mode 100644 index 00000000000..e8ba9e37242 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspectorWindow.rc @@ -0,0 +1,249 @@ +// Microsoft Visual C++ generated resource script. +// +#include "jaccessinspectorResource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +JACCESSINSPECTORWINDOW DIALOG 160, 78, 354, 214 +STYLE DS_SETFONT | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "jaccessinspector" +MENU cjaccessinspectorMenus +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT cjaccessinspectorText,4,24,340,184,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL + PUSHBUTTON "&First Message",cFirstMessage,4,4,60,14 + PUSHBUTTON "&Previous Message",cPreviousMessage,70,4,60,14 + EDITTEXT cMessageNumber,134,4,80,14,ES_CENTER | ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "&Next Message",cNextMessage,218,4,60,14 + PUSHBUTTON "&Last Message",cLastMessage,284,4,60,14 +END + +IDD_GO_TO_MESSAGE DIALOGEX 0, 0, 186, 66 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CAPTION +CAPTION "Go To Message" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Message Number:",IDC_STATIC,14,14,64,8 + EDITTEXT IDC_MESSAGE_NUMBER_EDIT,80,14,90,14,ES_AUTOHSCROLL | ES_NUMBER + DEFPUSHBUTTON "OK",IDOK,35,45,50,14 + PUSHBUTTON "Cancel",IDCANCEL,101,45,50,14 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "jaccessinspectorResource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + "JACCESSINSPECTORWINDOW", DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 347 + END + + IDD_GO_TO_MESSAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 59 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +cjaccessinspectorMenus MENU +BEGIN + POPUP "File" + BEGIN + MENUITEM "AccessBridge DLL Loaded", cAccessBridgeDLLLoaded + MENUITEM "Exit", cExitMenuItem + END + POPUP "UpdateSettings" + BEGIN + MENUITEM "Update from Mouse", cUpdateFromMouseMenuItem + MENUITEM "Update with F2 (mouse HWND)", cUpdateWithF2Item + MENUITEM "Update with F1 (mouse point)", cUpdateWithF1Item + END + POPUP "JavaEvents" + BEGIN + MENUITEM "Track Java Shutdown Events", cTrackShutdownMenuItem + MENUITEM SEPARATOR + MENUITEM "Track Mouse Entered Events", cTrackMouseMenuItem + MENUITEM "Track Mouse Exited Events", cTrackMouseExitedMenuItem + MENUITEM "Track Mouse Clicked Events", cTrackMouseClickedMenuItem + MENUITEM "Track Mouse Pressed Events", cTrackMousePressedMenuItem + MENUITEM "Track Mouse Released Events", cTrackMouseReleasedMenuItem + MENUITEM SEPARATOR + MENUITEM "Track Focus Gained Events", cTrackFocusMenuItem + MENUITEM "Track Focus Lost Events", cTrackFocusLostMenuItem + MENUITEM "Track Caret Events", cTrackCaretMenuItem + MENUITEM SEPARATOR + MENUITEM "Track Menu Selected Events", cTrackMenuSelectedMenuItem + MENUITEM "Track Menu Deselected Events", cTrackMenuDeselectedMenuItem + MENUITEM "Track Menu Canceled Events", cTrackMenuCanceledItem + MENUITEM "Track Popup Visible Events", cTrackPopupBecomeVisibleMenuItem + MENUITEM "Track Popup Inviible Events", cTrackPopupBecomeInvisibleMenuItem + MENUITEM "Track Popup Canceled Events", cTrackPopupCanceledItem + END + POPUP "AccessibilityEvents" + BEGIN + MENUITEM "Track Name Property Events", cTrackPropertyNameItem + MENUITEM "Track Description Property Events", cTrackPropertyDescriptionItem + MENUITEM "Track State Property Events", cTrackPropertyStateItem + MENUITEM "Track Value Property Events", cTrackPropertyValueItem + MENUITEM "Track Selection Property Events", cTrackPropertySelectionItem + MENUITEM "Track Text Property Events", cTrackPropertyTextItem + MENUITEM "Track Caret Property Events", cTrackPropertyCaretItem + MENUITEM "Track Visible Data Property Events", cTrackPropertyVisibleDataItem + MENUITEM "Track Child Property Events", cTrackPropertyChildItem + MENUITEM "Track Active Descendent Property Events", cTrackPropertyActiveDescendentItem + MENUITEM "Track Table Model Change Property Events", cTrackPropertyTableModelChangeItem + END + POPUP "&Options" + BEGIN + MENUITEM "&Monitor the same events as JAWS", cMonitorTheSameEventsAsJAWS + MENUITEM "Monitor &All Events", cMonitorAllEvents + MENUITEM "&Reset All Events", cResetAllEvents + MENUITEM "&Go To Message", cGoToMessage + MENUITEM "&Clear Message History", cClearMessageHistory + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +// Need 2 defines so macro argument to XSTR will get expanded before quoting. +#define XSTR(x) STR(x) +#define STR(x) #x + +VS_VERSION_INFO VERSIONINFO + FILEVERSION JDK_FVER + PRODUCTVERSION JDK_FVER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + // FILEOS 0x4 is Win32, 0x40004 is Win32 NT only + FILEOS 0x4L + // FILETYPE should be 0x1 for .exe and 0x2 for .dll + FILETYPE JDK_FTYPE + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", XSTR(JDK_COMPANY) "\0" + VALUE "FileDescription", XSTR(JDK_COMPONENT) "\0" + VALUE "FileVersion", XSTR(JDK_VER) "\0" + VALUE "Full Version", XSTR(JDK_BUILD_ID) "\0" + VALUE "InternalName", XSTR(JDK_INTERNAL_NAME) "\0" + VALUE "LegalCopyright", XSTR(JDK_COPYRIGHT) "\0" + VALUE "OriginalFilename", XSTR(JDK_FNAME) "\0" + VALUE "ProductName", XSTR(JDK_NAME) "\0" + VALUE "ProductVersion", XSTR(JDK_VER) "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR ACCELERATORS +BEGIN + VK_HOME, cFirstMessage, VIRTKEY, CONTROL, ALT, NOINVERT + "G", cGoToMessage, VIRTKEY, CONTROL, NOINVERT + VK_END, cLastMessage, VIRTKEY, CONTROL, ALT, NOINVERT + VK_NEXT, cNextMessage, VIRTKEY, CONTROL, NOINVERT + VK_PRIOR, cPreviousMessage, VIRTKEY, CONTROL, NOINVERT + "X", cClearMessageHistory, VIRTKEY, CONTROL, NOINVERT +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp b/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp new file mode 100644 index 00000000000..b8602f52f0d --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#include "jaccesswalker.h" +#include "AccessInfo.h" + +HWND ourHwnd; +HWND topLevelWindow; +int depth = -1; +FILE *logfile; +HMENU popupMenu; + +char theJaccesswalkerClassName[] = "JaccesswalkerWin"; +char theAccessInfoClassName[] = "AccessInfoWin"; + +HWND theJaccesswalkerWindow; +HWND theTreeControlWindow; +HINSTANCE theInstance; +Jaccesswalker *theJaccesswalker; +AccessibleNode *theSelectedNode; +AccessibleNode *thePopupNode; +AccessibleContext theSelectedAccessibleContext; +HWND hwndTV; // handle of tree-view control + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + + if (logfile == null) { + logfile = fopen(JACCESSWALKER_LOG, "w"); // overwrite existing log file + logString(logfile, "Starting jaccesswalker.exe %s\n", getTimeAndDate()); + } + + theInstance = hInstance; + + // start Jaccesswalker + theJaccesswalker = new Jaccesswalker(nCmdShow); + + return 0; +} + +Jaccesswalker::Jaccesswalker(int nCmdShow) { + + HWND hwnd; + static char szAppName[] = "jaccesswalker"; + static char szMenuName[] = "JACCESSWALKERMENU"; + MSG msg; + WNDCLASSEX wc; + + // jaccesswalker window + wc.cbSize = sizeof(wc); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WinProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = theInstance; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDI_APPLICATION); + wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wc.lpszMenuName = szMenuName; + wc.lpszClassName = szAppName; + wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + RegisterClassEx(&wc); + + // AccessInfo Window + wc.cbSize = sizeof(WNDCLASSEX); + + wc.hInstance = theInstance; + wc.lpszClassName = theAccessInfoClassName; + wc.lpfnWndProc = (WNDPROC)AccessInfoWindowProc; + wc.style = 0; + + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + + wc.lpszMenuName = ""; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + + wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); + + RegisterClassEx(&wc); + + // create the jaccesswalker window + hwnd = CreateWindow(szAppName, + szAppName, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + theInstance, + NULL); + + ourHwnd = hwnd; + + /* Initialize the common controls. */ + INITCOMMONCONTROLSEX cc; + cc.dwSize = sizeof(INITCOMMONCONTROLSEX); + cc.dwICC = ICC_TREEVIEW_CLASSES; + InitCommonControlsEx(&cc); + + ShowWindow(hwnd, nCmdShow); + + UpdateWindow(hwnd); + + BOOL result = initializeAccessBridge(); + if (result != FALSE) { + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + shutdownAccessBridge(); + } +} + +/* + * the jaccesswalker window proc + */ +LRESULT CALLBACK WinProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { + + int command; + short width, height; + + switch(iMsg) { + + case WM_CREATE: + // create the accessibility tree view + theTreeControlWindow = CreateATreeView(hwnd); + + // load the popup menu + popupMenu = LoadMenu(theInstance, "PopupMenu"); + popupMenu = GetSubMenu(popupMenu, 0); + break; + + case WM_CLOSE: + EndDialog(hwnd, TRUE); + PostQuitMessage (0); + break; + + case WM_SIZE: + width = LOWORD(lParam); + height = HIWORD(lParam); + SetWindowPos(theTreeControlWindow, NULL, 0, 0, width, height, 0); + return FALSE; // let windows finish handling this + + case WM_COMMAND: + command = LOWORD(wParam); + switch(command) { + + case cExitMenuItem: + EndDialog(hwnd, TRUE); + PostQuitMessage (0); + break; + + case cRefreshTreeItem: + // update the accessibility tree + theJaccesswalker->buildAccessibilityTree(); + break; + + case cAPIMenuItem: + // open a new window with the Accessibility API in it for the + // selected element in the tree + if (theSelectedNode != (AccessibleNode *) 0) { + theSelectedNode->displayAPIWindow(); + } + break; + + case cAPIPopupItem: + // open a new window with the Accessibility API in it for the + // element in the tree adjacent to the popup menu + if (thePopupNode != (AccessibleNode *) 0) { + thePopupNode->displayAPIWindow(); + } + break; + + } + break; + + case WM_NOTIFY: // receive tree messages + + NMTREEVIEW *nmptr = (LPNMTREEVIEW) lParam; + switch (nmptr->hdr.code) { + + case TVN_SELCHANGED: + // get the selected tree node + theSelectedNode = (AccessibleNode *) nmptr->itemNew.lParam; + break; + + case NM_RCLICK: + + // display a popup menu over the tree node + POINT p; + GetCursorPos(&p); + TrackPopupMenu(popupMenu, 0, p.x, p.y, 0, hwnd, NULL); + + // get the tree node under the popup menu + TVHITTESTINFO hitinfo; + ScreenToClient(theTreeControlWindow, &p); + hitinfo.pt = p; + HTREEITEM node = TreeView_HitTest(theTreeControlWindow, &hitinfo); + + if (node != null) { + TVITEMEX tvItem; + tvItem.hItem = node; + if (TreeView_GetItem(hwndTV, &tvItem) == TRUE) { + thePopupNode = (AccessibleNode *)tvItem.lParam; + } + } + break; + } + } + return DefWindowProc(hwnd, iMsg, wParam, lParam); +} + +/* + * Accessibility information window proc + */ +LRESULT CALLBACK AccessInfoWindowProc(HWND hWnd, UINT message, + UINT wParam, LONG lParam) { + short width, height; + HWND dlgItem; + + switch (message) { + case WM_CREATE: + RECT rcClient; // dimensions of client area + HWND hwndEdit; // handle of tree-view control + + // Get the dimensions of the parent window's client area, + // and create the edit control. + GetClientRect(hWnd, &rcClient); + hwndEdit = CreateWindow("Edit", + "", + WS_VISIBLE | WS_TABSTOP | WS_CHILD | + ES_MULTILINE | ES_AUTOVSCROLL | + ES_READONLY | WS_VSCROLL, + 0, 0, rcClient.right, rcClient.bottom, + hWnd, + (HMENU) cAccessInfoText, + theInstance, + NULL); + break; + + case WM_CLOSE: + DestroyWindow(hWnd); + break; + + case WM_SIZE: + width = LOWORD(lParam); + height = HIWORD(lParam); + dlgItem = GetDlgItem(hWnd, cAccessInfoText); + SetWindowPos(dlgItem, NULL, 0, 0, width, height, 0); + return FALSE; // let windows finish handling this + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; +} + +/** + * Build a tree (and the treeview control) of all accessible Java components + * + */ +void Jaccesswalker::buildAccessibilityTree() { + TreeView_DeleteAllItems (theTreeControlWindow); + // have MS-Windows call EnumWndProc() with all of the top-level windows + EnumWindows((WNDENUMPROC) EnumWndProc, NULL); +} + +/** + * Create (and display) the accessible component nodes of a parent AccessibleContext + * + */ +BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam) { + if (IsJavaWindow(hwnd)) { + long vmID; + AccessibleContext ac; + if (GetAccessibleContextFromHWND(hwnd, &vmID, &ac) == TRUE) { + theJaccesswalker->addComponentNodes(vmID, ac, (AccessibleNode *) NULL, + hwnd, TVI_ROOT, theTreeControlWindow); + } + topLevelWindow = hwnd; + } else { + char szClass [MAX_PATH] = {0}; + ::GetClassNameA(hwnd, szClass, sizeof(szClass) - 1); + if ( ( 0 == ::strcmp(szClass, "IEFrame") ) + || ( 0 == ::strcmp(szClass, "MozillaUIWindowClass") ) ) { + EnumChildWindows(hwnd, (WNDENUMPROC) EnumChildProc, NULL); + } + } + return TRUE; +} + +/* +Detects whether or not the specified Java window is one from which no useable +information can be obtained. + +This function tests for various scenarios I have seen in Java applets where the +Java applet had no meaningful accessible information. It does not detect all +scenarios, just the most common ones. +*/ +BOOL IsInaccessibleJavaWindow(const HWND hwnd) +{ + BOOL ret_val ( FALSE ); + { + BOOL bT ( FALSE ); + long vmIdWindow ( 0 ); + AccessibleContext acWindow ( 0 ); + bT = GetAccessibleContextFromHWND(hwnd, &vmIdWindow, &acWindow); + if ( ( bT ) && ( 0 != vmIdWindow ) && ( 0 != acWindow ) ) { + AccessibleContextInfo infoWindow = {0}; + bT = GetAccessibleContextInfo(vmIdWindow, acWindow, &infoWindow); + if ( ( bT ) + && ( 0 == infoWindow.name [0] ) + && ( 0 == infoWindow.description [0] ) + && ( 0 == ::wcscmp(infoWindow.role_en_US, L"frame") ) ) { + if ( 0 == infoWindow.childrenCount ) { + ret_val = TRUE; + } else if ( 1 == infoWindow.childrenCount ) { + AccessibleContext acChild ( 0 ); + acChild = + GetAccessibleChildFromContext(vmIdWindow, acWindow, 0); + if ( NULL != acChild ) { + AccessibleContextInfo infoChild = {0}; + bT = GetAccessibleContextInfo( vmIdWindow, acChild, + &infoChild ); + if ( ( bT ) + && ( 0 == infoChild.name [0] ) + && ( 0 == infoChild.description [0] ) + && ( 0 == ::wcscmp(infoChild.role_en_US, L"panel") ) + && ( 1 == infoChild.childrenCount ) ) { + AccessibleContext acChild1 ( 0 ); + acChild1 = GetAccessibleChildFromContext( vmIdWindow, + acChild, 0); + if ( NULL != acChild1 ) { + AccessibleContextInfo infoChild1 = {0}; + bT = GetAccessibleContextInfo( vmIdWindow, + acChild1, &infoChild1 ); + if ( ( bT ) + && ( 0 == infoChild1.name [0] ) + && ( 0 == infoChild1.description [0] ) + && ( 0 == ::wcscmp(infoChild1.role_en_US, L"frame") ) + && ( 0 == infoChild1.childrenCount ) ) { + ret_val = TRUE; + } else if ( ( bT ) + && ( 0 == infoChild1.name [0] ) + && ( 0 == infoChild1.description [0] ) + && ( 0 == ::wcscmp( infoChild1.role_en_US, + L"panel") ) + && ( 1 == infoChild1.childrenCount ) ) { + AccessibleContext acChild2 ( 0 ); + acChild2 = GetAccessibleChildFromContext( + vmIdWindow, acChild1, 0 ); + if ( NULL != acChild2 ) { + AccessibleContextInfo infoChild2 = {0}; + bT = GetAccessibleContextInfo( + vmIdWindow, acChild2, &infoChild2 ); + if ( ( bT ) + && ( 0 == infoChild2.name [0] ) + && ( 0 == infoChild2.description [0] ) + && ( 0 == ::wcscmp( infoChild2.role_en_US, + L"frame") ) + && ( 0 == infoChild2.childrenCount ) ) { + ret_val = TRUE; + } + } + } + } + } else if ( ( bT ) + && ( 0 == infoChild.name [0] ) + && ( 0 == infoChild.description [0] ) + && ( 0 == ::wcscmp( infoChild.role_en_US, + L"canvas") ) + && ( 0 == infoChild.childrenCount ) ) { + ret_val = TRUE; + } + } + } + } else if ( ( bT ) + && ( 0 == infoWindow.name [0] ) + && ( 0 == infoWindow.description [0] ) + && ( 0 == ::wcscmp(infoWindow.role_en_US, L"panel") ) ) { + if ( 1 == infoWindow.childrenCount ) { + AccessibleContext acChild ( 0 ); + acChild = GetAccessibleChildFromContext( vmIdWindow, + acWindow, 0 ); + if ( NULL != acChild ) { + AccessibleContextInfo infoChild = {0}; + bT = GetAccessibleContextInfo( vmIdWindow, + acChild, &infoChild ); + if ( ( bT ) + && ( 0 == infoChild.name [0] ) + && ( 0 == infoChild.description [0] ) + && ( 0 == ::wcscmp(infoChild.role_en_US, L"frame") ) + && ( 0 == infoChild.childrenCount ) ) { + ret_val = TRUE; + } else if ( ( bT ) + && ( 0 == infoChild.name [0] ) + && ( 0 == infoChild.description [0] ) + && ( 0 == ::wcscmp( infoChild.role_en_US, + L"panel") ) + && ( 1 == infoChild.childrenCount ) ) { + AccessibleContext acChild1 ( 0 ); + acChild1 = GetAccessibleChildFromContext( vmIdWindow, + acChild, 0); + if ( NULL != acChild1 ) { + AccessibleContextInfo infoChild1 = {0}; + bT = GetAccessibleContextInfo( vmIdWindow, + acChild1, + &infoChild1 ); + if ( ( bT ) + && ( 0 == infoChild1.name [0] ) + && ( 0 == infoChild1.description [0] ) + && ( 0 == ::wcscmp( infoChild1.role_en_US, + L"frame") ) + && ( 0 == infoChild1.childrenCount ) ) { + ret_val = TRUE; + } + } + } + } + } + } + } else if ( FALSE == bT ) { + ret_val = TRUE; + } + } + return ret_val; +} + +BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) +{ + if ( ( IsJavaWindow(hwnd) ) + && ( FALSE == IsInaccessibleJavaWindow(hwnd) ) ) { + long vmID ( 0 ); + AccessibleContext ac ( 0 ); + if ( TRUE == GetAccessibleContextFromHWND(hwnd, &vmID, &ac) ) { + theJaccesswalker->addComponentNodes( + vmID, ac, (AccessibleNode *) NULL, + hwnd, TVI_ROOT, theTreeControlWindow); + } + topLevelWindow = hwnd; + } else { + EnumChildWindows(hwnd, (WNDENUMPROC) EnumChildProc, NULL); + } + return TRUE; +} + +// CreateATreeView - creates a tree-view control. +// Returns the handle of the new control if successful or NULL +// otherwise. +// hwndParent - handle of the control's parent window +HWND CreateATreeView(HWND hwndParent) { + RECT rcClient; // dimensions of client area + + // Get the dimensions of the parent window's client area, and create + // the tree-view control. + GetClientRect(hwndParent, &rcClient); + hwndTV = CreateWindow(WC_TREEVIEW, + "", + WS_VISIBLE | WS_TABSTOP | WS_CHILD | + TVS_HASLINES | TVS_HASBUTTONS | + TVS_LINESATROOT, + 0, 0, rcClient.right, rcClient.bottom, + hwndParent, + (HMENU) cTreeControl, + theInstance, + NULL); + + return hwndTV; +} + +/** + * Create (and display) the accessible component nodes of a parent AccessibleContext + * + */ +void Jaccesswalker::addComponentNodes(long vmID, AccessibleContext context, + AccessibleNode *parent, HWND hwnd, + HTREEITEM treeNodeParent, HWND treeWnd) { + + AccessibleNode *newNode = new AccessibleNode( vmID, context, parent, hwnd, + treeNodeParent ); + + AccessibleContextInfo info; + if (GetAccessibleContextInfo(vmID, context, &info) != FALSE) { + char s[LINE_BUFSIZE]; + + wsprintf(s, "%ls", info.name); + newNode->setAccessibleName(s); + wsprintf(s, "%ls", info.role); + newNode->setAccessibleRole(s); + + wsprintf(s, "%ls [%ls]", info.name, info.role); + + TVITEM tvi; + tvi.mask = TVIF_PARAM | TVIF_TEXT; + tvi.pszText = (char *) s; // Accessible name and role + tvi.cchTextMax = (int)strlen(s); + tvi.lParam = (long) newNode; // Accessibility information + + TVINSERTSTRUCT tvis; + tvis.hParent = treeNodeParent; + tvis.hInsertAfter = TVI_LAST; + tvis.item = tvi; + + HTREEITEM treeNodeItem = TreeView_InsertItem(treeWnd, &tvis); + + for (int i = 0; i < info.childrenCount; i++) { + addComponentNodes(vmID, GetAccessibleChildFromContext(vmID, context, i), + newNode, hwnd, treeNodeItem, treeWnd); + } + } else { + char s[LINE_BUFSIZE]; + sprintf( s, + "ERROR calling GetAccessibleContextInfo; vmID = %X, context = %X", + vmID, context ); + + TVITEM tvi; + tvi.mask = TVIF_PARAM | TVIF_TEXT; // text and lParam are only valid parts + tvi.pszText = (char *) s; + tvi.cchTextMax = (int)strlen(s); + tvi.lParam = (long) newNode; + + TVINSERTSTRUCT tvis; + tvis.hParent = treeNodeParent; + tvis.hInsertAfter = TVI_LAST; // make tree in order given + tvis.item = tvi; + + HTREEITEM treeNodeItem = TreeView_InsertItem(treeWnd, &tvis); + } +} + +// ----------------------------- + +/** + * Create an AccessibleNode + * + */ +AccessibleNode::AccessibleNode(long JavaVMID, AccessibleContext context, + AccessibleNode *parent, HWND hwnd, + HTREEITEM parentTreeNodeItem) { + vmID = JavaVMID; + ac = context; + parentNode = parent; + baseHWND = hwnd; + treeNodeParent = parentTreeNodeItem; + + // setting accessibleName and accessibleRole not done here, + // in order to minimize calls to the AccessBridge + // (since such a call is needed to enumerate children) +} + +/** + * Destroy an AccessibleNode + * + */ +AccessibleNode::~AccessibleNode() { + ReleaseJavaObject(vmID, ac); +} + +/** + * Set the accessibleName string + * + */ +void AccessibleNode::setAccessibleName(char *name) { + strncpy(accessibleName, name, MAX_STRING_SIZE); +} + +/** + * Set the accessibleRole string + * + */ +void AccessibleNode::setAccessibleRole(char *role) { + strncpy(accessibleRole, role, SHORT_STRING_SIZE); +} + + + + + + + +/** + * Create an API window to show off the info for this AccessibleContext + */ +BOOL AccessibleNode::displayAPIWindow() { + + HWND apiWindow = CreateWindow(theAccessInfoClassName, + "Java Accessibility API view", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + 600, + 750, + HWND_DESKTOP, + NULL, + theInstance, + (void *) NULL); + + if (!apiWindow) { + printError("cannot create API window"); + return FALSE; + } + + char buffer[HUGE_BUFSIZE]; + buffer[0] = '\0'; + getAccessibleInfo(vmID, ac, buffer, sizeof(buffer)); + displayAndLog(apiWindow, cAccessInfoText, logfile, buffer); + + ShowWindow(apiWindow, SW_SHOWNORMAL); + UpdateWindow(apiWindow); + + return TRUE; +} + + + diff --git a/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.h b/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.h new file mode 100644 index 00000000000..b6dd9b8765a --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#include // includes basic windows functionality +#include +#include +#include +#include "jaccesswalkerResource.h" +#include "AccessBridgeCalls.h" +#include "AccessBridgeCallbacks.h" +#include "AccessBridgeDebug.h" + +#include +#include +#include +#include +#include +#include + +#include + +extern FILE *file; + +#define null NULL +#define JACCESSWALKER_LOG "jaccesswalker.log" + +/** + * A node in the jaccesswalker tree + */ +class AccessibleNode { + + HWND baseHWND; + HTREEITEM treeNodeParent; + long vmID; + AccessibleContext ac; + AccessibleNode *parentNode; + char accessibleName[MAX_STRING_SIZE]; + char accessibleRole[SHORT_STRING_SIZE]; + +public: + AccessibleNode(long vmID, AccessibleContext context, + AccessibleNode *parent, HWND hWnd, + HTREEITEM parentTreeNodeItem); + ~AccessibleNode(); + void setAccessibleName(char *name); + void setAccessibleRole(char *role); + BOOL displayAPIWindow(); // bring up an Accessibility API detail window +}; + + +/** + * The main application class + */ +class Jaccesswalker { + +public: + Jaccesswalker(int nCmdShow); + BOOL InitWindow(int windowMode); + char *getAccessibleInfo( long vmID, AccessibleContext ac, char *buffer, + int bufsize ); + void exitjaccesswalker(HWND hWnd); + void buildAccessibilityTree(); + void addComponentNodes( long vmID, AccessibleContext context, + AccessibleNode *parent, HWND hWnd, + HTREEITEM treeNodeParent, HWND treeWnd ); +}; + +char *getTimeAndDate(); + +void displayAndLogText(char *buffer, ...); + +LRESULT CALLBACK WinProc (HWND, UINT, WPARAM, LPARAM); + +void debugString(char *msg, ...); + +LRESULT CALLBACK jaccesswalkerWindowProc( HWND hDlg, UINT message, UINT wParam, + LONG lParam ); + +BOOL CALLBACK EnumWndProc(HWND hWnd, LPARAM lParam); +BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam); + +HWND CreateATreeView(HWND hwndParent); + +LRESULT CALLBACK AccessInfoWindowProc( HWND hWnd, UINT message, UINT wParam, + LONG lParam ); + +char *getAccessibleInfo( long vmID, AccessibleContext ac, char *buffer, + int bufsize ); diff --git a/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalkerResource.h b/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalkerResource.h new file mode 100644 index 00000000000..905e13924c3 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalkerResource.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#define cTreeControl 1001 +#define cAccessInfoText 2001 +#define cMonkeyMenus 10000 +#define cFileMenu 10100 +#define cRefreshTreeItem 10101 +#define cExitMenuItem 10103 +#define cSettingsMenu 10200 +#define cAPIMenuItem 10201 +#define cAPIPopupItem 10202 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40003 +#define _APS_NEXT_CONTROL_VALUE 1032 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalkerWindow.rc b/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalkerWindow.rc new file mode 100644 index 00000000000..372e7b83bc7 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalkerWindow.rc @@ -0,0 +1,182 @@ +//Microsoft Developer Studio generated resource script. +// +#include "jaccesswalkerResource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +JACCESSWALKERWINDOW DIALOG DISCARDABLE 160, 78, 294, 214 +STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "jaccesswalker" +MENU 10000 +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Tree1",cTreeControl,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | + WS_BORDER | WS_TABSTOP,4,0,283,214 +END + +EXPLORERWINDOW DIALOG DISCARDABLE 160, 78, 294, 214 +STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Java Accessibility Information" +MENU 10000 +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT cAccessInfoText,4,0,283,214,ES_MULTILINE | ES_AUTOVSCROLL | + ES_READONLY | WS_VSCROLL +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "jaccesswalkerResource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + "JACCESSWALKERWINDOW", DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 287 + END + + "ACCESSINFOWINDOW", DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 287 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +JACCESSWALKERMENU MENU DISCARDABLE +BEGIN + POPUP "File" + BEGIN + MENUITEM "Refresh Tree", cRefreshTreeItem + MENUITEM SEPARATOR + MENUITEM "Exit", cExitMenuItem + END + POPUP "Panels" + BEGIN + MENUITEM "Display Accessibility Information", cAPIMenuItem + END +END + + +PopupMenu MENU + { + POPUP "" + { + MENUITEM "Display Accessibility Information", cAPIPopupItem + } + } + + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +// Need 2 defines so macro argument to XSTR will get expanded before quoting. +#define XSTR(x) STR(x) +#define STR(x) #x + +VS_VERSION_INFO VERSIONINFO + FILEVERSION JDK_FVER + PRODUCTVERSION JDK_FVER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + // FILEOS 0x4 is Win32, 0x40004 is Win32 NT only + FILEOS 0x4L + // FILETYPE should be 0x1 for .exe and 0x2 for .dll + FILETYPE JDK_FTYPE + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", XSTR(JDK_COMPANY) "\0" + VALUE "FileDescription", XSTR(JDK_COMPONENT) "\0" + VALUE "FileVersion", XSTR(JDK_VER) "\0" + VALUE "Full Version", XSTR(JDK_BUILD_ID) "\0" + VALUE "InternalName", XSTR(JDK_INTERNAL_NAME) "\0" + VALUE "LegalCopyright", XSTR(JDK_COPYRIGHT) "\0" + VALUE "OriginalFilename", XSTR(JDK_FNAME) "\0" + VALUE "ProductName", XSTR(JDK_NAME) "\0" + VALUE "ProductVersion", XSTR(JDK_VER) "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/jdk/src/jdk.accessibility/windows/native/libjavaaccessbridge/AccessBridgeJavaEntryPoints.cpp b/jdk/src/jdk.accessibility/windows/native/libjavaaccessbridge/AccessBridgeJavaEntryPoints.cpp index 75a7b6d8d0d..c1054b48f76 100644 --- a/jdk/src/jdk.accessibility/windows/native/libjavaaccessbridge/AccessBridgeJavaEntryPoints.cpp +++ b/jdk/src/jdk.accessibility/windows/native/libjavaaccessbridge/AccessBridgeJavaEntryPoints.cpp @@ -3487,10 +3487,15 @@ AccessBridgeJavaEntryPoints::getAccessibleTextInfo(jobject accessibleContext, // Get the index at the given point if (getAccessibleIndexAtPointFromContextMethod != (jmethodID) 0) { - textInfo->indexAtPoint = jniEnv->CallIntMethod(accessBridgeObject, - getAccessibleIndexAtPointFromContextMethod, - accessibleContext, x, y); - EXCEPTION_CHECK("Getting AccessibleIndexAtPoint - call to CallIntMethod()", FALSE); + // If x or y is -1 return -1 + if (x == -1 || y == -1) { + textInfo->indexAtPoint = -1; + } else { + textInfo->indexAtPoint = jniEnv->CallIntMethod(accessBridgeObject, + getAccessibleIndexAtPointFromContextMethod, + accessibleContext, x, y); + EXCEPTION_CHECK("Getting AccessibleIndexAtPoint - call to CallIntMethod()", FALSE); + } PrintDebugString(" Index at point = %d", textInfo->indexAtPoint); } else { PrintDebugString(" Error! either env == 0 or getAccessibleIndexAtPointFromContextMethod == 0"); diff --git a/jdk/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.cpp b/jdk/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.cpp new file mode 100644 index 00000000000..564c2e29bce --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.cpp @@ -0,0 +1,948 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#include "AccessBridgeCalls.h" +#include "AccessInfo.h" +#include +#include + +LogStringCallbackFP g_LogStringCallback = NULL; + +/* + * returns formatted date and time + */ +char *getTimeAndDate() { + static char datebuf[64]; + struct tm *newtime; + char am_pm[] = "AM"; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + if( newtime->tm_hour > 12 ) /* Set up extension. */ + strcpy( am_pm, "PM" ); + if( newtime->tm_hour > 12 ) /* Convert from 24-hour */ + newtime->tm_hour -= 12; /* to 12-hour clock. */ + if( newtime->tm_hour == 0 ) /*Set hour to 12 if midnight. */ + newtime->tm_hour = 12; + + sprintf(datebuf, "%.19s %s\n", asctime( newtime ), am_pm ); + return (char *)datebuf; +} + + +/* + * displays a message in a dialog and writes the message to a logfile + */ +void displayAndLog(HWND hDlg, int nIDDlgItem, FILE *logfile, char *msg, ...) { + + if (hDlg == NULL || msg == NULL) { + return; + } + + char tmpbuf[HUGE_BUFSIZE]; + va_list argprt; + + va_start(argprt, msg); + vsprintf(tmpbuf, msg, argprt); + + SetDlgItemText(hDlg, nIDDlgItem, tmpbuf); + + fprintf(logfile, "\n****************************************\n"); + fprintf(logfile, "%s\n", getTimeAndDate()); + fprintf(logfile, "%s\n", tmpbuf); + fflush(logfile); + + if ( NULL != g_LogStringCallback ) + { + g_LogStringCallback (tmpbuf); + } +} + +/* + * writes a text string to a logfile + */ +void logString(FILE *logfile, char *msg, ...) { + + if (logfile == NULL || msg == NULL) { + return; + } + + char tmpbuf[LINE_BUFSIZE]; + va_list argprt; + + va_start(argprt, msg); + vsprintf(tmpbuf, msg, argprt); + + fprintf(logfile, tmpbuf); + fprintf(logfile, "\n"); + fflush(logfile); +} + +/* + * safely appends a message to a buffer + */ +BOOL appendToBuffer(char *buf, size_t buflen, char *msg, ...) { + + static char warning[] = + "\nNot enough buffer space; remaining information truncated.\n"; + size_t warningLength = strlen(warning) + 1; + + if (buf == NULL || msg == NULL) { + return FALSE; + } + + char tmpbuf[LARGE_BUFSIZE]; + va_list argprt; + + va_start(argprt, msg); + vsprintf(tmpbuf, msg, argprt); + + // verify there's enough space in the buffer + size_t spaceRemaining = buflen - strlen(buf) - 1; + if (spaceRemaining <= warningLength) { + strncat(buf, warning, spaceRemaining); + return FALSE; + } + strncat(buf, tmpbuf, spaceRemaining); + return TRUE; +} + +/** + * returns accessibility information for an AccessibleContext + */ +char *getAccessibleInfo(long vmID, AccessibleContext ac, char *buffer, + int bufsize) { + return getAccessibleInfo(vmID, ac, 0, 0, buffer, bufsize); +} + +/** + * returns accessibility information at the specified coordinates in an + * AccessibleContext + */ +char *getAccessibleInfo(long vmID, AccessibleContext ac, int x, int y, + char *buffer, int bufsize) { + + wchar_t tmpBuf[LINE_BUFSIZE]; + wchar_t name[LINE_BUFSIZE]; + int i, j; + long start; + long end; + + if (buffer == NULL || bufsize <= 0) { + return NULL; + } + buffer[0] = NULL; + + /* ===== AccessBridge / J2SE version information ===== */ + + AccessBridgeVersionInfo versionInfo; + BOOL result = GetVersionInfo(vmID, &versionInfo); + + if (result == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: cannot get version information", bufsize ); + } else { + appendToBuffer(buffer, bufsize, "Version Information:"); + appendToBuffer( buffer, bufsize, + "\r\n Java virtual machine version: %ls", + versionInfo.VMversion ); + appendToBuffer( buffer, bufsize, + "\r\n Access Bridge Java class version: %ls", + versionInfo.bridgeJavaClassVersion ); + appendToBuffer( buffer, bufsize, + "\r\n Access Bridge Java DLL version: %ls", + versionInfo.bridgeJavaDLLVersion ); + appendToBuffer( buffer, bufsize, + "\r\n Access Bridge Windows DLL version: %ls", + versionInfo.bridgeWinDLLVersion ); + } + + if (ac == (AccessibleContext) 0) { + return buffer; + } + + + /* ===== core AccessibleContext information ===== */ + + AccessibleContextInfo info; + if (GetAccessibleContextInfo(vmID, ac, &info) == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetAccessibleContextInfo failed ", bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessibleContext information", bufsize ); + if (x >= 0 && y >= 0) { + appendToBuffer(buffer, bufsize, " at mouse point [%d, %d]:", x, y); + } else { + appendToBuffer(buffer, bufsize, ":", bufsize); + } + + appendToBuffer(buffer, bufsize, "\r\n Name: %ls", info.name); + if ( getVirtualAccessibleName( vmID, ac, name, + (sizeof(name) / sizeof(wchar_t)) ) == FALSE ) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nERROR: getVirtualAccessibleName", bufsize ); + } else { + appendToBuffer(buffer, bufsize, "\r\n Virtual Name: %ls", name); + } + appendToBuffer( buffer, bufsize, "\r\n Description: %ls", + info.description ); + appendToBuffer(buffer, bufsize, "\r\n Role: %ls", info.role); + appendToBuffer( buffer, bufsize, "\r\n Role in en_US locale: %ls", + info.role_en_US ); + appendToBuffer(buffer, bufsize, "\r\n States: %ls", info.states); + appendToBuffer( buffer, bufsize, "\r\n States in en_US locale: %ls", + info.states_en_US ); + appendToBuffer( buffer, bufsize, "\r\n Index in parent: %d", + info.indexInParent ); + appendToBuffer( buffer, bufsize, "\r\n Children count: %d", + info.childrenCount ); + appendToBuffer( buffer, bufsize, + "\r\n Bounding rectangle: [%d, %d, %d, %d]", + info.x, info.y, info.x + info.width, + info.y + info.height ); + + /* top-level window info */ + AccessibleContext topAC = getTopLevelObject(vmID, ac); + if (topAC == NULL) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: getTopLevelObject failed", bufsize ); + } else { + AccessibleContextInfo topInfo; + if (GetAccessibleContextInfo(vmID, topAC, &topInfo) == FALSE) { + appendToBuffer( + buffer, bufsize, + "\r\nERROR: GetAccessibleContextInfo failed for top-level window ", + bufsize ); + } else { + if (getVirtualAccessibleName(vmID, topAC, name, + (sizeof(name) / sizeof(wchar_t)) ) == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nERROR: getVirtualAccessibleName", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n Top-level window name: %ls", + name ); + } + appendToBuffer( buffer, bufsize, + "\r\n Top-level window role: %ls", + topInfo.role ) ; + } + ReleaseJavaObject(vmID, topAC); + + } + + /* ===== AccessibleParent information ===== */ + + AccessibleContext parentAC = GetAccessibleParentFromContext(vmID, ac); + if (parentAC == NULL) { + appendToBuffer(buffer, bufsize, "\r\n No parent", bufsize); + } else { + AccessibleContextInfo parentInfo; + if (GetAccessibleContextInfo(vmID, parentAC, &parentInfo) == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetAccessibleContextInfo failed for parent", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, "\r\n Parent name: %ls", + parentInfo.name ); + if ( getVirtualAccessibleName( vmID, parentAC, name, + (sizeof(name) / sizeof(wchar_t)) ) == FALSE ) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nERROR: getVirtualAccessibleName", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n Parent virtual name: %ls", name ); + } + appendToBuffer( buffer, bufsize, "\r\n Parent role: %ls", + parentInfo.role ); + } + ReleaseJavaObject(vmID, parentAC); + } + + + /* ====== visible children ===== */ + int nChildren = getVisibleChildrenCount(vmID, ac); + if (nChildren == -1) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetVisibleChildrenCount failed", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n Visible descendents count: %d", + nChildren ); + } + + if (nChildren > 0) { + VisibleChildrenInfo visibleChildrenInfo; + if (getVisibleChildren(vmID, ac, 0, &visibleChildrenInfo) == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetVisibleChildren failed", + bufsize ); + } else { + AccessibleContextInfo childACInfo; + for ( int child = 0; + child < visibleChildrenInfo.returnedChildrenCount; + child++ ) { + AccessibleContext childAC = + visibleChildrenInfo.children[child]; + if (GetAccessibleContextInfo(vmID, childAC, &childACInfo)) { + if ( getVirtualAccessibleName( vmID, childAC, name, + (sizeof(name) / sizeof(wchar_t))) == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nERROR: getVirtualAccessibleName", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n Descendent %d name: %ls", child, + name ); + } + appendToBuffer( buffer, bufsize, + "\r\n Descendent %d role: %ls", + child, childACInfo.role ); + } + ReleaseJavaObject(vmID, childAC); + } + } + } + + /* ===== AccessibleSelection ===== */ + + if (info.accessibleSelection == TRUE) { + + int selCount; + AccessibleContext selectedAC; + + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessible Selection information:", + bufsize ); + + if ((selCount = GetAccessibleSelectionCountFromContext(vmID, ac)) != -1) { + appendToBuffer( buffer, bufsize, "\r\n Selection count: %d", + selCount ); + + for (i = 0; i < selCount; i++) { + if ( ( selectedAC = + GetAccessibleSelectionFromContext(vmID, ac, i) ) == NULL ) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetAccessibleSelectionFromContext failed forselection %d", + i ); + } else { + if (GetAccessibleContextInfo(vmID, selectedAC, &info) == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetAccessibleContextInfo failed for selection %d", i); + } else { + if ( getVirtualAccessibleName( vmID, selectedAC, name, + (sizeof(name) / sizeof(wchar_t)) ) == FALSE ) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nERROR: getVirtualAccessibleName", bufsize); + } else { + appendToBuffer( buffer, bufsize, + "\r\n Selection %d name: %ls", i, name ); + } + appendToBuffer( buffer, bufsize, + "\r\n Selection %d role: %ls", i, info.role); + appendToBuffer( buffer, bufsize, + "\r\n Index in parent of selection %d: %d", + i, info.indexInParent ); + } + ReleaseJavaObject(vmID, selectedAC); + } + } + } + } + + // ====== Accessible KeyBindings, Icons and Actions ====== + + AccessibleKeyBindings keyBindings; + if (getAccessibleKeyBindings(vmID, ac, &keyBindings) == TRUE && + keyBindings.keyBindingsCount > 0) { + + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessibleKeyBinding info:", bufsize ); + appendToBuffer( buffer, bufsize, + "\r\n Number of key bindings: %d", + keyBindings.keyBindingsCount ); + + for (j = 0; j < keyBindings.keyBindingsCount; j++) { + + appendToBuffer( buffer, bufsize, + "\r\n Key binding %d character: %c", j, + keyBindings.keyBindingInfo[j].character ); + appendToBuffer( buffer, bufsize, + "\r\n Key binding %d modifiers: %d", j, + keyBindings.keyBindingInfo[j].modifiers ); + } + } + + AccessibleIcons icons; + if (getAccessibleIcons(vmID, ac, &icons) == TRUE && + icons.iconsCount > 0) { + + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessibleIcons info:", bufsize ); + appendToBuffer( buffer, bufsize, + "\r\n Number of icons: %d", icons.iconsCount ); + + for (j = 0; j < icons.iconsCount; j++) { + + appendToBuffer( buffer, bufsize, + "\r\n Icon %d description: %ls", j, + icons.iconInfo[j].description ); + appendToBuffer( buffer, bufsize, + "\r\n Icon %d height: %d", j, + icons.iconInfo[j].height ); + appendToBuffer( buffer, bufsize, + "\r\n Icon %d width: %d", j, + icons.iconInfo[j].width ); + } + } + + AccessibleActions actions; + if (getAccessibleActions(vmID, ac, &actions) == TRUE && + actions.actionsCount > 0) { + + appendToBuffer( buffer, bufsize, "\r\n\r\nAccessibleActions info:", + bufsize ); + appendToBuffer( buffer, bufsize, "\r\n Number of actions: %d", + actions.actionsCount ); + + for (j = 0; j < actions.actionsCount; j++) { + appendToBuffer( buffer, bufsize, "\r\n Action %d name: %ls", + j, actions.actionInfo[j].name ); + } + } + + /* ===== AccessibleRelationSet ===== */ + + AccessibleRelationSetInfo relationSetInfo; + if (getAccessibleRelationSet(vmID, ac, &relationSetInfo) == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\nGetAccessibleRelationSet failed.", bufsize ); + } else { + int i; + AccessibleContextInfo relInfo; + + if (relationSetInfo.relationCount > 0) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessibleRelationSet information:", + bufsize ); + appendToBuffer( buffer, bufsize, + "\r\n Number of relations: %d", + relationSetInfo.relationCount ); + } + for (i = 0; i < relationSetInfo.relationCount; i++) { + AccessibleRelationInfo relationInfo = + relationSetInfo.relations[i]; + + appendToBuffer( buffer, bufsize, + "\r\n Relation %d key: %ls", i, + relationInfo.key ); + appendToBuffer( buffer, bufsize, + "\r\n Relation %d target count: %d", i, + relationInfo.targetCount ); + for (j = 0; j < relationInfo.targetCount; j++) { + if (GetAccessibleContextInfo( + vmID, relationInfo.targets[j], &relInfo ) == FALSE) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetAccessibleContextInfo failed.", + bufsize ); + } else { + // Core AccessibleContext information for relation target + if ( getVirtualAccessibleName( vmID, relationInfo.targets[j], + name, (sizeof(name) / sizeof(wchar_t)) ) == FALSE ) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nERROR: getVirtualAccessibleName", bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n Target %d name: %ls", + j, name ); + } + appendToBuffer( buffer, bufsize, + "\r\n Target %d role: %ls", j, + relInfo.role); + } + ReleaseJavaObject(vmID, relationInfo.targets[j]); + + } + } + } + + /* ===== AccessibleValue ===== */ + + if (info.accessibleInterfaces & cAccessibleValueInterface) { + + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessible Value information:", bufsize); + + if ( GetCurrentAccessibleValueFromContext( vmID, ac, tmpBuf, + (sizeof(tmpBuf) / sizeof(wchar_t)) ) == TRUE ) { + appendToBuffer( buffer, bufsize, "\r\n Current Value: %ls", + tmpBuf ); + } + if ( GetMaximumAccessibleValueFromContext( vmID, ac, tmpBuf, + (sizeof(tmpBuf) / sizeof(wchar_t))) == TRUE ) { + appendToBuffer( buffer, bufsize, + "\r\n Maximum Value: %ls", tmpBuf ); + } + if ( GetMinimumAccessibleValueFromContext( vmID, ac, tmpBuf, + (sizeof(tmpBuf) / sizeof(wchar_t)) ) == TRUE ) { + appendToBuffer( buffer, bufsize, + "\r\n Minimum Value: %ls", tmpBuf ); + } + } + + + /* ===== AccessibleTable ===== */ + + AccessibleTableInfo tableInfo; + + if ( (info.accessibleInterfaces & cAccessibleTableInterface) == + cAccessibleTableInterface ) { + if (getAccessibleTableInfo(vmID, ac, &tableInfo) != TRUE) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: getAccessibleTableInfo failed", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessibleTable info:", bufsize ); + + int trow = getAccessibleTableRow( vmID, + tableInfo.accessibleTable, 3 ); + appendToBuffer( buffer, bufsize, + "\r\n getAccessibleTableRow: %d", trow); + + int tcol = + getAccessibleTableColumn(vmID, tableInfo.accessibleTable, 2); + appendToBuffer( buffer, bufsize, + "\r\n getAccessibleTableColumn: %d", tcol ); + + int tindex = getAccessibleTableIndex( vmID, + tableInfo.accessibleTable, + 2, 3 ); + appendToBuffer( buffer, bufsize, + "\r\n getAccessibleTableIndex: %d", + tindex ); + + // Core info + appendToBuffer( buffer, bufsize, + "\r\n table row count: %d", + tableInfo.rowCount ); + appendToBuffer( buffer, bufsize, + "\r\n table column count: %d", + tableInfo.columnCount ); + + AccessibleTableCellInfo tableCellInfo; + for (int i = 0; i < tableInfo.rowCount; i++) { + for (int j = 0; j < tableInfo.columnCount; j++) { + + if ( !getAccessibleTableCellInfo( vmID, + tableInfo.accessibleTable, + i, j, + &tableCellInfo ) ) { + + appendToBuffer( + buffer, bufsize, + "\r\nERROR: GetAccessibleTableCellInfo failed.", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n\r\n AccessibleTable cell[%d,%d] info:", + i, j ); + appendToBuffer( buffer, bufsize, + "\r\n Index: %ld", tableCellInfo.index ); + appendToBuffer( buffer, bufsize, + "\r\n Row extent: %ld", + tableCellInfo.rowExtent ); + appendToBuffer( buffer, bufsize, + "\r\n Column extent: %ld", + tableCellInfo.columnExtent ); + appendToBuffer( buffer, bufsize, + "\r\n Is selected?: %ld", + tableCellInfo.isSelected ); + + AccessibleContextInfo cellACInfo; + if ( !GetAccessibleContextInfo( + vmID, + tableCellInfo.accessibleContext, + &cellACInfo ) ) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetAccessibleContextInfo failed for table cell [%d,%d].", + i, j ); + } else { + if ( !getVirtualAccessibleName( vmID, + tableCellInfo.accessibleContext, name, + (sizeof(name) / sizeof(wchar_t)) ) ) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nERROR: getVirtualAccessibleName", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n Name: %ls", name ); + } + appendToBuffer( buffer, bufsize, + "\r\n Role: %ls", + cellACInfo.role ); + } + ReleaseJavaObject( vmID, + tableCellInfo.accessibleContext ); + } + } + } + + // Get the column headers + AccessibleTableInfo columnInfo; + if ( !getAccessibleTableColumnHeader(vmID, ac, &columnInfo)) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: getAccessibleTableColumnHeader failed.", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessibleTable column header info:", bufsize ); + + // Core info + appendToBuffer( buffer, bufsize, + "\r\n Column header row count: %d", + columnInfo.rowCount ); + appendToBuffer( buffer, bufsize, + "\r\n Column header column count: %d", + columnInfo.columnCount ); + + } + + // Get the selected rows + int numSelections = + getAccessibleTableRowSelectionCount( vmID, + tableInfo.accessibleTable ); + appendToBuffer( buffer, bufsize, + "\r\n\r\nRow selection count: %d", + numSelections ); + jint *selections = new jint[numSelections]; + + if ( !getAccessibleTableRowSelections( vmID, + tableInfo.accessibleTable, + numSelections, + selections ) ) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: getAccessibleTableRowSelections failed.", + bufsize ); + } else { + appendToBuffer(buffer, bufsize, " \r\n Row selections: "); + for (int j = 0; j < numSelections; j++) { + appendToBuffer(buffer, bufsize, " %d", selections[j]); + } + } + + // Get column header info + for (int i = 0; i < columnInfo.columnCount; i++) { + if ( !getAccessibleTableCellInfo( vmID, + columnInfo.accessibleTable, + 0, i, &tableCellInfo ) ) { + + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetAccessibleTableCellInfo failed.", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n\r\nColumn header [0,%d] cell info.", i ); + appendToBuffer( buffer, bufsize, + "\r\n Index: %ld", tableCellInfo.index ); + appendToBuffer( buffer, bufsize, + "\r\n Row extent: %ld", + tableCellInfo.rowExtent ); + appendToBuffer( buffer, bufsize, + "\r\n Column extent: %ld", + tableCellInfo.columnExtent ); + appendToBuffer( buffer, bufsize, + "\r\n Is selected: %ld", + tableCellInfo.isSelected ); + + AccessibleContextInfo cellACInfo; + if ( !GetAccessibleContextInfo( vmID, + tableCellInfo.accessibleContext, &cellACInfo ) ) { + appendToBuffer( buffer, bufsize, + "\r\nERROR: GetAccessibleContextInfo failed.", + bufsize ); + } else { + if ( !getVirtualAccessibleName( vmID, + tableCellInfo.accessibleContext, name, + (sizeof(name) / sizeof(wchar_t)) ) ) { + appendToBuffer( buffer, bufsize, + "\r\n\r\nERROR: getVirtualAccessibleName", + bufsize ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n Name: %ls", name ); + } + appendToBuffer( buffer, bufsize, + "\r\n Role: %ls", cellACInfo.role ); + } + ReleaseJavaObject(vmID, tableCellInfo.accessibleContext); + } + } + } + } + + /* ===== AccessibleText ===== */ + + if (info.accessibleText == TRUE) { + AccessibleTextInfo textInfo; + AccessibleTextItemsInfo textItems; + AccessibleTextSelectionInfo textSelection; + AccessibleTextRectInfo rectInfo; + AccessibleTextAttributesInfo attributeInfo; + + appendToBuffer( buffer, bufsize, + "\r\n\r\nAccessible Text information:", bufsize); + + if (GetAccessibleTextInfo(vmID, ac, &textInfo, x, y) == TRUE) { + appendToBuffer( buffer, bufsize, + "\r\n Mouse point at text index: %d", + textInfo.indexAtPoint ); + appendToBuffer( buffer, bufsize, + "\r\n Caret at text index: %d", + textInfo.caretIndex ); + appendToBuffer( buffer, bufsize, + "\r\n Char count: %d", + textInfo.charCount ); + } + if ( GetAccessibleTextSelectionInfo(vmID, ac, &textSelection) ) { + + appendToBuffer( buffer, bufsize, + "\r\n Selection start index: %d", + textSelection.selectionStartIndex ); + appendToBuffer( buffer, bufsize, + "\r\n Selection end index: %d", + textSelection.selectionEndIndex ); + appendToBuffer( buffer, bufsize, + "\r\n Selected text: %ls", + textSelection.selectedText ); + } + + /* ===== AccessibleText information at the mouse point ===== */ + + appendToBuffer( buffer, bufsize, + "\r\n\r\n At mouse point index: %d", textInfo.indexAtPoint); + + if (GetAccessibleTextRect(vmID, ac, &rectInfo, textInfo.indexAtPoint)) { + + appendToBuffer( buffer, bufsize, + "\r\n Character bounding rectangle: [%d, %d, %d, %d]", + rectInfo.x, rectInfo.y, rectInfo.width, rectInfo.height ); + } + + if ( GetAccessibleTextLineBounds( vmID, ac, textInfo.indexAtPoint, + &start, &end ) ) { + if ( GetAccessibleTextRange( vmID, ac, start, end, tmpBuf, + (sizeof(tmpBuf) / sizeof(wchar_t)) ) ) { + appendToBuffer( buffer, bufsize, + "\r\n Line bounds: [%d, %d]", start, end); + } + } + if ( GetAccessibleTextItems( vmID, ac, &textItems, + textInfo.indexAtPoint ) ) { + + appendToBuffer( buffer, bufsize, + "\r\n Character: %lc", textItems.letter ); + appendToBuffer( buffer, bufsize, + "\r\n Word: %ls", textItems.word ); + appendToBuffer( buffer, bufsize, + "\r\n Sentence: %ls", textItems.sentence ); + } + + /* ===== AccessibleText attributes ===== */ + + if (GetAccessibleTextAttributes(vmID, ac, + textInfo.indexAtPoint, + &attributeInfo)) { + + appendToBuffer( buffer, bufsize, "\r\n Core attributes: %s", + attributeInfo.bold ? "bold" : "not bold" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.italic ? "italic" : "not italic" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.underline ? "underline" : "not underline" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.strikethrough ? "strikethrough" : + "not strikethrough" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.superscript ? "superscript" : + "not superscript" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.subscript ? "subscript" : "not subscript" ); + + appendToBuffer( buffer, bufsize, + "\r\n Background color: %ls", + attributeInfo.backgroundColor ); + appendToBuffer( buffer, bufsize, + "\r\n Foreground color: %ls", + attributeInfo.foregroundColor ); + appendToBuffer( buffer, bufsize, + "\r\n Font family: %ls", + attributeInfo.fontFamily ); + appendToBuffer( buffer, bufsize, + "\r\n Font size: %d", + attributeInfo.fontSize ); + + appendToBuffer( buffer, bufsize, + "\r\n First line indent: %f", + attributeInfo.firstLineIndent ); + appendToBuffer( buffer, bufsize, + "\r\n Left indent: %f", + attributeInfo.leftIndent ); + appendToBuffer( buffer, bufsize, + "\r\n Right indent: %f", + attributeInfo.rightIndent ); + appendToBuffer( buffer, bufsize, + "\r\n Line spacing: %f", + attributeInfo.lineSpacing ); + appendToBuffer( buffer, bufsize, + "\r\n Space above: %f", + attributeInfo.spaceAbove ); + appendToBuffer( buffer, bufsize, + "\r\n Space below: %f", + attributeInfo.spaceBelow ); + + appendToBuffer( buffer, bufsize, + "\r\n Full attribute string: %ls", + attributeInfo.fullAttributesString ); + + // get the attribute run length + short runLength = -1; + if ( getTextAttributesInRange( vmID, ac, textInfo.indexAtPoint, + textInfo.indexAtPoint + 100, + &attributeInfo, &runLength ) ) { + appendToBuffer( buffer, bufsize, + "\r\n Attribute run: %d", runLength ); + } else { + appendToBuffer( buffer, bufsize, + "\r\n getTextAttributesInRangeFailed" ); + } + } + + /* ===== AccessibleText information at the caret index ===== */ + + appendToBuffer( buffer, bufsize, + "\r\n\r\n At caret index: %d", textInfo.caretIndex); + + if (getCaretLocation(vmID, ac, &rectInfo, textInfo.caretIndex)) { + appendToBuffer( buffer, bufsize, + "\r\n Caret bounding rectangle: [%d, %d, %d, %d]", + rectInfo.x, rectInfo.y, rectInfo.width, rectInfo.height ); + } + + if (GetAccessibleTextRect(vmID, ac, &rectInfo, textInfo.caretIndex)) { + + appendToBuffer( buffer, bufsize, + "\r\n Character bounding rectangle: [%d, %d, %d, %d]", + rectInfo.x, rectInfo.y, rectInfo.width, rectInfo.height ); + } + + if ( GetAccessibleTextLineBounds( vmID, ac, textInfo.caretIndex, + &start, &end ) ) { + if ( GetAccessibleTextRange( vmID, ac, start, end, tmpBuf, + (sizeof(tmpBuf) / sizeof(wchar_t)) ) ) { + + appendToBuffer( buffer, bufsize, + "\r\n Line bounds: [%d, %d]", start, end); + } + } + if (GetAccessibleTextItems(vmID, ac, &textItems, textInfo.caretIndex)) { + + appendToBuffer( buffer, bufsize, + "\r\n Character: %lc", textItems.letter ); + appendToBuffer( buffer, bufsize, + "\r\n Word: %ls", textItems.word ); + appendToBuffer( buffer, bufsize, + "\r\n Sentence: %ls", textItems.sentence ); + } + + /* ===== AccessibleText attributes ===== */ + + if (GetAccessibleTextAttributes(vmID, ac, textInfo.caretIndex, &attributeInfo)) { + + appendToBuffer( buffer, bufsize, "\r\n Core attributes: %s", + attributeInfo.bold ? "bold" : "not bold" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.italic ? "italic" : "not italic" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.underline ? "underline" : "not underline" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.strikethrough ? "strikethrough" : + "not strikethrough" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.superscript ? "superscript" : + "not superscript" ); + appendToBuffer( buffer, bufsize, ", %s", + attributeInfo.subscript ? "subscript" : "not subscript"); + + appendToBuffer( buffer, bufsize, + "\r\n Background color: %ls", + attributeInfo.backgroundColor ); + appendToBuffer( buffer, bufsize, + "\r\n Foreground color: %ls", + attributeInfo.foregroundColor ); + appendToBuffer( buffer, bufsize, + "\r\n Font family: %ls", attributeInfo.fontFamily ); + appendToBuffer( buffer, bufsize, + "\r\n Font size: %d", attributeInfo.fontSize); + + + appendToBuffer( buffer, bufsize, + "\r\n First line indent: %f", + attributeInfo.firstLineIndent ); + appendToBuffer( buffer, bufsize, + "\r\n Left indent: %f", attributeInfo.leftIndent ); + appendToBuffer( buffer, bufsize, + "\r\n Right indent: %f", attributeInfo.rightIndent ); + appendToBuffer( buffer, bufsize, + "\r\n Line spacing: %f", attributeInfo.lineSpacing ); + appendToBuffer( buffer, bufsize, + "\r\n Space above: %f", attributeInfo.spaceAbove ); + appendToBuffer( buffer, bufsize, + "\r\n Space below: %f", attributeInfo.spaceBelow ); + appendToBuffer( buffer, bufsize, + "\r\n Full attribute string: %ls", + attributeInfo.fullAttributesString ); + // get the attribute run length + short runLength = -1; + if ( getTextAttributesInRange( vmID, ac, textInfo.caretIndex, + textInfo.caretIndex + 100, + &attributeInfo, &runLength ) ) { + appendToBuffer( buffer, bufsize, + "\r\n Attribute run: %d", runLength); + } else { + appendToBuffer( buffer, bufsize, + "\r\n getTextAttributesInRangeFailed" ); + } + } + } + } + return buffer; +} diff --git a/jdk/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.h b/jdk/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.h new file mode 100644 index 00000000000..a749ebc9784 --- /dev/null +++ b/jdk/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +typedef void (WINAPI * LogStringCallbackFP) (const char * lpString); +extern LogStringCallbackFP g_LogStringCallback; + +#define LINE_BUFSIZE 1024 +#define LARGE_BUFSIZE 5120 +#define HUGE_BUFSIZE 20480 + +/* + * returns formatted date and time + */ +char *getTimeAndDate(); + +/* + * displays a message in a dialog and writes the message to a logfile + */ +void displayAndLog(HWND hDlg, int nIDDlgItem, FILE *logfile, char *msg, ...); + +/* + * writes a text string to a logfile + */ +void logString(FILE *logfile, char *msg, ...); + +/** + * returns accessibility information for an AccessibleContext + */ +char *getAccessibleInfo(long vmID, AccessibleContext ac, char *buffer, int bufsize); + +/** + * returns accessibility information at the specified coordinates in an AccessibleContext + */ +char *getAccessibleInfo(long vmID, AccessibleContext ac, int x, int y, char *buffer, int bufsize); From 08c1fe55c0fdf99fb6dc309e213839e4d0f3798e Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Sun, 22 Nov 2015 17:27:33 +0300 Subject: [PATCH 046/199] 8135100: Behavior of null arguments not specified in javax.sound.sampled.spi The specification change was reviewed by Florian Bomers also Reviewed-by: amenkov --- .../com/sun/media/sound/AiffFileWriter.java | 9 +- .../com/sun/media/sound/AlawCodec.java | 4 +- .../com/sun/media/sound/AuFileWriter.java | 9 +- .../sound/AudioFloatFormatConverter.java | 6 +- .../sound/DirectAudioDeviceProvider.java | 5 +- .../com/sun/media/sound/PCMtoPCMCodec.java | 4 +- .../sun/media/sound/PortMixerProvider.java | 7 +- .../com/sun/media/sound/UlawCodec.java | 5 +- .../com/sun/media/sound/WaveFileWriter.java | 9 +- .../javax/sound/sampled/AudioSystem.java | 123 +++--- .../sound/sampled/spi/AudioFileReader.java | 6 + .../sound/sampled/spi/AudioFileWriter.java | 14 +- .../sampled/spi/FormatConversionProvider.java | 25 +- .../sound/sampled/spi/MixerProvider.java | 16 +- .../AudioFileReader}/AudioFileClose.java | 0 .../AudioFileReader/ExpectedNPEOnNull.java | 120 ++++++ .../AudioFileReader}/ReadersExceptions.java | 0 .../RepeatedFormatReader.java | 0 .../AudioFileWriter}/AlawEncoderSync.java | 0 .../AudioFileWriter/ExpectedNPEOnNull.java | 310 +++++++++++++++ .../AudioFileWriter}/WriterCloseInput.java | 0 .../ExpectedNPEOnNull.java | 356 ++++++++++++++++++ .../spi/MixerProvider/ExpectedNPEOnNull.java | 88 +++++ 23 files changed, 1040 insertions(+), 76 deletions(-) rename jdk/test/javax/sound/sampled/{FileReader => spi/AudioFileReader}/AudioFileClose.java (100%) create mode 100644 jdk/test/javax/sound/sampled/spi/AudioFileReader/ExpectedNPEOnNull.java rename jdk/test/javax/sound/sampled/{FileReader => spi/AudioFileReader}/ReadersExceptions.java (100%) rename jdk/test/javax/sound/sampled/{FileReader => spi/AudioFileReader}/RepeatedFormatReader.java (100%) rename jdk/test/javax/sound/sampled/{FileWriter => spi/AudioFileWriter}/AlawEncoderSync.java (100%) create mode 100644 jdk/test/javax/sound/sampled/spi/AudioFileWriter/ExpectedNPEOnNull.java rename jdk/test/javax/sound/sampled/{FileWriter => spi/AudioFileWriter}/WriterCloseInput.java (100%) create mode 100644 jdk/test/javax/sound/sampled/spi/FormatConversionProvider/ExpectedNPEOnNull.java create mode 100644 jdk/test/javax/sound/sampled/spi/MixerProvider/ExpectedNPEOnNull.java diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AiffFileWriter.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AiffFileWriter.java index b31871f3bb5..5e9e3c45cce 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AiffFileWriter.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AiffFileWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -37,6 +37,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.RandomAccessFile; import java.io.SequenceInputStream; +import java.util.Objects; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioInputStream; @@ -84,6 +85,9 @@ public final class AiffFileWriter extends SunFileWriter { public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); //$$fb the following check must come first ! Otherwise // the next frame length check may throw an IOException and @@ -103,6 +107,9 @@ public final class AiffFileWriter extends SunFileWriter { public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); // throws IllegalArgumentException if not supported AiffFileFormat aiffFileFormat = (AiffFileFormat)getAudioFileFormat(fileType, stream); diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AlawCodec.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AlawCodec.java index fbe63f6ff16..fcc0e709f2b 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AlawCodec.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AlawCodec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -26,6 +26,7 @@ package com.sun.media.sound; import java.io.IOException; +import java.util.Objects; import java.util.Vector; import javax.sound.sampled.AudioFormat; @@ -119,6 +120,7 @@ public final class AlawCodec extends SunCodec { /** */ public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat){ + Objects.requireNonNull(sourceFormat); if( (targetEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) && sourceFormat.getEncoding().equals( AudioFormat.Encoding.ALAW)) || (targetEncoding.equals( AudioFormat.Encoding.ALAW) && sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_SIGNED)) ) { return getOutputFormats( sourceFormat ); diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AuFileWriter.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AuFileWriter.java index 3020c348953..1c2ccf5b74c 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AuFileWriter.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AuFileWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -37,6 +37,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.RandomAccessFile; import java.io.SequenceInputStream; +import java.util.Objects; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioInputStream; @@ -83,6 +84,9 @@ public final class AuFileWriter extends SunFileWriter { public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); // we must know the total data length to calculate the file length //$$fb 2001-07-13: fix for bug 4351296: do not throw an exception @@ -100,6 +104,9 @@ public final class AuFileWriter extends SunFileWriter { public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); // throws IllegalArgumentException if not supported AuFileFormat auFileFormat = (AuFileFormat)getAudioFileFormat(fileType, stream); diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java index a161fc5db42..a1e522afb40 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.Objects; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -538,6 +539,7 @@ public final class AudioFloatFormatConverter extends FormatConversionProvider { public AudioFormat[] getTargetFormats(Encoding targetEncoding, AudioFormat sourceFormat) { + Objects.requireNonNull(targetEncoding); if (AudioFloatConverter.getConverter(sourceFormat) == null) return new AudioFormat[0]; int channels = sourceFormat.getChannels(); @@ -592,6 +594,7 @@ public final class AudioFloatFormatConverter extends FormatConversionProvider { public boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) { + Objects.requireNonNull(targetFormat); if (AudioFloatConverter.getConverter(sourceFormat) == null) return false; if (AudioFloatConverter.getConverter(targetFormat) == null) @@ -605,6 +608,7 @@ public final class AudioFloatFormatConverter extends FormatConversionProvider { public boolean isConversionSupported(Encoding targetEncoding, AudioFormat sourceFormat) { + Objects.requireNonNull(targetEncoding); if (AudioFloatConverter.getConverter(sourceFormat) == null) return false; for (int i = 0; i < formats.length; i++) { diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/DirectAudioDeviceProvider.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/DirectAudioDeviceProvider.java index c306dc78c66..274571917ea 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/DirectAudioDeviceProvider.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/DirectAudioDeviceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -121,7 +121,8 @@ public final class DirectAudioDeviceProvider extends MixerProvider { } } } - throw new IllegalArgumentException("Mixer " + info.toString() + " not supported by this provider."); + throw new IllegalArgumentException( + String.format("Mixer %s not supported by this provider", info)); } diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/PCMtoPCMCodec.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/PCMtoPCMCodec.java index dbed920e6fa..6ef8268f34c 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/PCMtoPCMCodec.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/PCMtoPCMCodec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -26,6 +26,7 @@ package com.sun.media.sound; import java.io.IOException; +import java.util.Objects; import java.util.Vector; import javax.sound.sampled.AudioFormat; @@ -87,6 +88,7 @@ public final class PCMtoPCMCodec extends SunCodec { /** */ public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat){ + Objects.requireNonNull(targetEncoding); // filter out targetEncoding from the old getOutputFormats( sourceFormat ) method diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/PortMixerProvider.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/PortMixerProvider.java index a8ef40f8a0f..ce7f3596a48 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/PortMixerProvider.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/PortMixerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -101,7 +101,6 @@ public final class PortMixerProvider extends MixerProvider { } } - public Mixer getMixer(Mixer.Info info) { synchronized (PortMixerProvider.class) { for (int i = 0; i < infos.length; i++) { @@ -110,8 +109,8 @@ public final class PortMixerProvider extends MixerProvider { } } } - throw new IllegalArgumentException("Mixer " + info.toString() - + " not supported by this provider."); + throw new IllegalArgumentException( + String.format("Mixer %s not supported by this provider", info)); } diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/UlawCodec.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/UlawCodec.java index 5dde45b734b..dc088ee459c 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/UlawCodec.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/UlawCodec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -27,6 +27,7 @@ package com.sun.media.sound; import java.io.IOException; +import java.util.Objects; import java.util.Vector; import javax.sound.sampled.AudioFormat; @@ -106,6 +107,8 @@ public final class UlawCodec extends SunCodec { /** */ public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat){ + Objects.requireNonNull(targetEncoding); + Objects.requireNonNull(sourceFormat); if( (AudioFormat.Encoding.PCM_SIGNED.equals(targetEncoding) && AudioFormat.Encoding.ULAW.equals(sourceFormat.getEncoding())) || diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFileWriter.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFileWriter.java index b9becd23d5a..199364c94f4 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFileWriter.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/WaveFileWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -37,6 +37,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.RandomAccessFile; import java.io.SequenceInputStream; +import java.util.Objects; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioInputStream; @@ -106,6 +107,9 @@ public final class WaveFileWriter extends SunFileWriter { public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); //$$fb the following check must come first ! Otherwise // the next frame length check may throw an IOException and @@ -127,6 +131,9 @@ public final class WaveFileWriter extends SunFileWriter { public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); // throws IllegalArgumentException if not supported WaveFileFormat waveFileFormat = (WaveFileFormat)getAudioFileFormat(fileType, stream); diff --git a/jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java b/jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java index bb5e72666ba..2a6d98dd158 100644 --- a/jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java +++ b/jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java @@ -33,6 +33,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.Vector; @@ -191,50 +192,21 @@ public class AudioSystem { * mixer installed on the system * @see #getMixerInfo */ - public static Mixer getMixer(Mixer.Info info) { - - Mixer mixer = null; - List providers = getMixerProviders(); - - for(int i = 0; i < providers.size(); i++ ) { - + public static Mixer getMixer(final Mixer.Info info) { + for (final MixerProvider provider : getMixerProviders()) { try { - return providers.get(i).getMixer(info); - - } catch (IllegalArgumentException e) { - } catch (NullPointerException e) { - // $$jb 08.20.99: If the strings in the info object aren't - // set, then Netscape (using jdk1.1.5) tends to throw - // NPE's when doing some string manipulation. This is - // probably not the best fix, but is solves the problem - // of the NPE in Netscape using local classes - // $$jb 11.01.99: Replacing this patch. + return provider.getMixer(info); + } catch (IllegalArgumentException | NullPointerException ignored) { + // The MixerProvider.getMixer(null) should return default Mixer, + // This behaviour was assumed from the beginning, but strictly + // specified only in the jdk9. Since the jdk1.1.5 we skipped + // NPE for some reason and therefore skipped some + // implementations of MixerProviders, which throw NPE. To keep + // support of such implementations, we still ignore NPE. } } - - //$$fb if looking for default mixer, and not found yet, add a round of looking - if (info == null) { - for(int i = 0; i < providers.size(); i++ ) { - try { - MixerProvider provider = providers.get(i); - Mixer.Info[] infos = provider.getMixerInfo(); - // start from 0 to last device (do not reverse this order) - for (int ii = 0; ii < infos.length; ii++) { - try { - return provider.getMixer(infos[ii]); - } catch (IllegalArgumentException e) { - // this is not a good default device :) - } - } - } catch (IllegalArgumentException e) { - } catch (NullPointerException e) { - } - } - } - - - throw new IllegalArgumentException("Mixer not supported: " - + (info!=null?info.toString():"null")); + throw new IllegalArgumentException( + String.format("Mixer not supported: %s", info)); } //$$fb 2002-11-26: fix for 4757930: DOC: AudioSystem.getTarget/SourceLineInfo() is ambiguous @@ -696,8 +668,10 @@ public class AudioSystem { * array of length 0 is returned. Otherwise, the array will have a * length of at least 1, representing {@code sourceEncoding} * (no conversion). + * @throws NullPointerException if {@code sourceEncoding} is {@code null} */ public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding sourceEncoding) { + Objects.requireNonNull(sourceEncoding); List codecs = getFormatConversionProviders(); Vector encodings = new Vector<>(); @@ -730,9 +704,10 @@ public class AudioSystem { * array of length 0 is returned. Otherwise, the array will have a * length of at least 1, representing the encoding of * {@code sourceFormat} (no conversion). + * @throws NullPointerException if {@code sourceFormat} is {@code null} */ public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) { - + Objects.requireNonNull(sourceFormat); List codecs = getFormatConversionProviders(); Vector encodings = new Vector<>(); @@ -769,9 +744,12 @@ public class AudioSystem { * @param sourceFormat the audio format before conversion * @return {@code true} if the conversion is supported, otherwise * {@code false} + * @throws NullPointerException if {@code targetEncoding} or + * {@code sourceFormat} are {@code null} */ public static boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) { - + Objects.requireNonNull(targetEncoding); + Objects.requireNonNull(sourceFormat); List codecs = getFormatConversionProviders(); @@ -792,6 +770,8 @@ public class AudioSystem { * @param sourceStream the stream to be converted * @return an audio input stream of the indicated encoding * @throws IllegalArgumentException if the conversion is not supported + * @throws NullPointerException if {@code targetEncoding} or + * {@code sourceStream} are {@code null} * @see #getTargetEncodings(AudioFormat.Encoding) * @see #getTargetEncodings(AudioFormat) * @see #isConversionSupported(AudioFormat.Encoding, AudioFormat) @@ -799,6 +779,8 @@ public class AudioSystem { */ public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream) { + Objects.requireNonNull(targetEncoding); + Objects.requireNonNull(sourceStream); List codecs = getFormatConversionProviders(); @@ -821,8 +803,12 @@ public class AudioSystem { * @param sourceFormat the audio format before conversion * @return array of formats. If no formats of the specified encoding are * supported, an array of length 0 is returned. + * @throws NullPointerException if {@code targetEncoding} or + * {@code sourceFormat} are {@code null} */ public static AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) { + Objects.requireNonNull(targetEncoding); + Objects.requireNonNull(sourceFormat); List codecs = getFormatConversionProviders(); Vector formats = new Vector<>(); @@ -860,8 +846,12 @@ public class AudioSystem { * @param sourceFormat the audio format before conversion * @return {@code true} if the conversion is supported, otherwise * {@code false} + * @throws NullPointerException if {@code targetFormat} or + * {@code sourceFormat} are {@code null} */ public static boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) { + Objects.requireNonNull(targetFormat); + Objects.requireNonNull(sourceFormat); List codecs = getFormatConversionProviders(); @@ -882,6 +872,8 @@ public class AudioSystem { * @param sourceStream the stream to be converted * @return an audio input stream of the indicated format * @throws IllegalArgumentException if the conversion is not supported + * @throws NullPointerException if {@code targetFormat} or + * {@code sourceStream} are {@code null} * @see #getTargetEncodings(AudioFormat) * @see #getTargetFormats(AudioFormat.Encoding, AudioFormat) * @see #isConversionSupported(AudioFormat, AudioFormat) @@ -889,7 +881,6 @@ public class AudioSystem { */ public static AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream) { - if (sourceStream.getFormat().matches(targetFormat)) { return sourceStream; } @@ -924,11 +915,13 @@ public class AudioSystem { * @throws UnsupportedAudioFileException if the stream does not point to * valid audio file data recognized by the system * @throws IOException if an input/output exception occurs + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ public static AudioFileFormat getAudioFileFormat(InputStream stream) - throws UnsupportedAudioFileException, IOException { + throws UnsupportedAudioFileException, IOException { + Objects.requireNonNull(stream); List providers = getAudioFileReaders(); AudioFileFormat format = null; @@ -961,9 +954,11 @@ public class AudioSystem { * @throws UnsupportedAudioFileException if the URL does not point to valid * audio file data recognized by the system * @throws IOException if an input/output exception occurs + * @throws NullPointerException if {@code url} is {@code null} */ public static AudioFileFormat getAudioFileFormat(URL url) - throws UnsupportedAudioFileException, IOException { + throws UnsupportedAudioFileException, IOException { + Objects.requireNonNull(url); List providers = getAudioFileReaders(); AudioFileFormat format = null; @@ -996,9 +991,11 @@ public class AudioSystem { * @throws UnsupportedAudioFileException if the {@code File} does not point * to valid audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ public static AudioFileFormat getAudioFileFormat(File file) - throws UnsupportedAudioFileException, IOException { + throws UnsupportedAudioFileException, IOException { + Objects.requireNonNull(file); List providers = getAudioFileReaders(); AudioFileFormat format = null; @@ -1037,11 +1034,13 @@ public class AudioSystem { * @throws UnsupportedAudioFileException if the stream does not point to * valid audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ public static AudioInputStream getAudioInputStream(InputStream stream) - throws UnsupportedAudioFileException, IOException { + throws UnsupportedAudioFileException, IOException { + Objects.requireNonNull(stream); List providers = getAudioFileReaders(); AudioInputStream audioStream = null; @@ -1074,9 +1073,11 @@ public class AudioSystem { * @throws UnsupportedAudioFileException if the URL does not point to valid * audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code url} is {@code null} */ public static AudioInputStream getAudioInputStream(URL url) - throws UnsupportedAudioFileException, IOException { + throws UnsupportedAudioFileException, IOException { + Objects.requireNonNull(url); List providers = getAudioFileReaders(); AudioInputStream audioStream = null; @@ -1109,9 +1110,11 @@ public class AudioSystem { * @throws UnsupportedAudioFileException if the {@code File} does not point * to valid audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ public static AudioInputStream getAudioInputStream(File file) - throws UnsupportedAudioFileException, IOException { + throws UnsupportedAudioFileException, IOException { + Objects.requireNonNull(file); List providers = getAudioFileReaders(); AudioInputStream audioStream = null; @@ -1163,9 +1166,10 @@ public class AudioSystem { * @param fileType the file type for which write capabilities are queried * @return {@code true} if the file type is supported, otherwise * {@code false} + * @throws NullPointerException if {@code fileType} is {@code null} */ public static boolean isFileTypeSupported(AudioFileFormat.Type fileType) { - + Objects.requireNonNull(fileType); List providers = getAudioFileWriters(); for(int i=0; i < providers.size(); i++) { @@ -1185,8 +1189,10 @@ public class AudioSystem { * support is queried * @return array of file types. If no file types are supported, an array of * length 0 is returned. + * @throws NullPointerException if {@code stream} is {@code null} */ public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) { + Objects.requireNonNull(stream); List providers = getAudioFileWriters(); Set returnTypesSet = new HashSet<>(); @@ -1210,10 +1216,13 @@ public class AudioSystem { * @param stream the stream for which file-writing support is queried * @return {@code true} if the file type is supported for this audio input * stream, otherwise {@code false} + * @throws NullPointerException if {@code fileType} or {@code stream} are + * {@code null} */ public static boolean isFileTypeSupported(AudioFileFormat.Type fileType, AudioInputStream stream) { - + Objects.requireNonNull(fileType); + Objects.requireNonNull(stream); List providers = getAudioFileWriters(); for(int i=0; i < providers.size(); i++) { @@ -1241,11 +1250,16 @@ public class AudioSystem { * @throws IOException if an input/output exception occurs * @throws IllegalArgumentException if the file type is not supported by the * system + * @throws NullPointerException if {@code stream} or {@code fileType} or + * {@code out} are {@code null} * @see #isFileTypeSupported * @see #getAudioFileTypes */ public static int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); List providers = getAudioFileWriters(); int bytesWritten = 0; @@ -1281,11 +1295,16 @@ public class AudioSystem { * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file type is not supported by the * system + * @throws NullPointerException if {@code stream} or {@code fileType} or + * {@code out} are {@code null} * @see #isFileTypeSupported * @see #getAudioFileTypes */ public static int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); List providers = getAudioFileWriters(); int bytesWritten = 0; diff --git a/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileReader.java b/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileReader.java index 7df8f998bc9..f130dba477f 100644 --- a/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileReader.java +++ b/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileReader.java @@ -60,6 +60,7 @@ public abstract class AudioFileReader { * @throws UnsupportedAudioFileException if the stream does not point to * valid audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ @@ -77,6 +78,7 @@ public abstract class AudioFileReader { * @throws UnsupportedAudioFileException if the URL does not point to valid * audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code url} is {@code null} */ public abstract AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException; @@ -92,6 +94,7 @@ public abstract class AudioFileReader { * @throws UnsupportedAudioFileException if the {@code File} does not point * to valid audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ public abstract AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException; @@ -112,6 +115,7 @@ public abstract class AudioFileReader { * @throws UnsupportedAudioFileException if the stream does not point to * valid audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ @@ -129,6 +133,7 @@ public abstract class AudioFileReader { * @throws UnsupportedAudioFileException if the URL does not point to valid * audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code url} is {@code null} */ public abstract AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException; @@ -144,6 +149,7 @@ public abstract class AudioFileReader { * @throws UnsupportedAudioFileException if the {@code File} does not point * to valid audio file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ public abstract AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException; diff --git a/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileWriter.java b/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileWriter.java index 03a44bbc084..7ce970b2508 100644 --- a/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileWriter.java +++ b/jdk/src/java.desktop/share/classes/javax/sound/sampled/spi/AudioFileWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -28,6 +28,7 @@ package javax.sound.sampled.spi; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.util.Objects; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; @@ -60,8 +61,10 @@ public abstract class AudioFileWriter { * @param fileType the file type for which write capabilities are queried * @return {@code true} if the file type is supported, otherwise * {@code false} + * @throws NullPointerException if {@code fileType} is {@code null} */ public boolean isFileTypeSupported(Type fileType) { + Objects.requireNonNull(fileType); Type types[] = getAudioFileTypes(); @@ -81,6 +84,7 @@ public abstract class AudioFileWriter { * is queried * @return array of file types. If no file types are supported, an array of * length 0 is returned. + * @throws NullPointerException if {@code stream} is {@code null} */ public abstract Type[] getAudioFileTypes(AudioInputStream stream); @@ -92,9 +96,11 @@ public abstract class AudioFileWriter { * @param stream for which file writing support is queried * @return {@code true} if the file type is supported for this audio input * stream, otherwise {@code false} + * @throws NullPointerException if {@code fileType} or {@code stream} are + * {@code null} */ public boolean isFileTypeSupported(Type fileType, AudioInputStream stream) { - + Objects.requireNonNull(fileType); Type types[] = getAudioFileTypes( stream ); for(int i=0; i * The full set of the mixer info objects that represent the mixers * supported by this {@code MixerProvider} may be obtained through the * {@code getMixerInfo} method. Use the {@code isMixerSupported} method to * test whether this {@code MixerProvider} supports a particular mixer. * - * @param info an info object that describes the desired mixer + * @param info an info object that describes the desired mixer, or + * {@code null} for the default mixer * @return mixer instance * @throws IllegalArgumentException if the info object specified does not - * match the info object for a mixer supported by this MixerProvider + * match the info object for a mixer supported by this + * {@code MixerProvider}, or if this {@code MixerProvider} does not + * have default mixer, but default mixer has been requested * @see #getMixerInfo() * @see #isMixerSupported(Mixer.Info) */ diff --git a/jdk/test/javax/sound/sampled/FileReader/AudioFileClose.java b/jdk/test/javax/sound/sampled/spi/AudioFileReader/AudioFileClose.java similarity index 100% rename from jdk/test/javax/sound/sampled/FileReader/AudioFileClose.java rename to jdk/test/javax/sound/sampled/spi/AudioFileReader/AudioFileClose.java diff --git a/jdk/test/javax/sound/sampled/spi/AudioFileReader/ExpectedNPEOnNull.java b/jdk/test/javax/sound/sampled/spi/AudioFileReader/ExpectedNPEOnNull.java new file mode 100644 index 00000000000..c0a89c926c1 --- /dev/null +++ b/jdk/test/javax/sound/sampled/spi/AudioFileReader/ExpectedNPEOnNull.java @@ -0,0 +1,120 @@ +/* + * 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.io.File; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.spi.AudioFileReader; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8135100 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testAS(); + testAFR(); + } + + /** + * Tests the part of AudioSystem API, which implemented via AudioFileReader. + */ + private static void testAS() throws Exception { + + try { + AudioSystem.getAudioFileFormat((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + try { + AudioSystem.getAudioFileFormat((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + try { + AudioSystem.getAudioFileFormat((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + try { + AudioSystem.getAudioInputStream((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + try { + AudioSystem.getAudioInputStream((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + try { + AudioSystem.getAudioInputStream((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests the AudioFileReader API directly. + */ + private static void testAFR() throws Exception { + + for (final AudioFileReader afr : load(AudioFileReader.class)) { + try { + afr.getAudioFileFormat((InputStream) null); + throw new RuntimeException("NPE is expected: " + afr); + } catch (final NullPointerException ignored) { + } + try { + afr.getAudioFileFormat((URL) null); + throw new RuntimeException("NPE is expected: " + afr); + } catch (final NullPointerException ignored) { + } + try { + afr.getAudioFileFormat((File) null); + throw new RuntimeException("NPE is expected: " + afr); + } catch (final NullPointerException ignored) { + } + try { + afr.getAudioInputStream((InputStream) null); + throw new RuntimeException("NPE is expected: " + afr); + } catch (final NullPointerException ignored) { + } + try { + afr.getAudioInputStream((URL) null); + throw new RuntimeException("NPE is expected: " + afr); + } catch (final NullPointerException ignored) { + } + try { + afr.getAudioInputStream((File) null); + throw new RuntimeException("NPE is expected: " + afr); + } catch (final NullPointerException ignored) { + } + } + } +} diff --git a/jdk/test/javax/sound/sampled/FileReader/ReadersExceptions.java b/jdk/test/javax/sound/sampled/spi/AudioFileReader/ReadersExceptions.java similarity index 100% rename from jdk/test/javax/sound/sampled/FileReader/ReadersExceptions.java rename to jdk/test/javax/sound/sampled/spi/AudioFileReader/ReadersExceptions.java diff --git a/jdk/test/javax/sound/sampled/FileReader/RepeatedFormatReader.java b/jdk/test/javax/sound/sampled/spi/AudioFileReader/RepeatedFormatReader.java similarity index 100% rename from jdk/test/javax/sound/sampled/FileReader/RepeatedFormatReader.java rename to jdk/test/javax/sound/sampled/spi/AudioFileReader/RepeatedFormatReader.java diff --git a/jdk/test/javax/sound/sampled/FileWriter/AlawEncoderSync.java b/jdk/test/javax/sound/sampled/spi/AudioFileWriter/AlawEncoderSync.java similarity index 100% rename from jdk/test/javax/sound/sampled/FileWriter/AlawEncoderSync.java rename to jdk/test/javax/sound/sampled/spi/AudioFileWriter/AlawEncoderSync.java diff --git a/jdk/test/javax/sound/sampled/spi/AudioFileWriter/ExpectedNPEOnNull.java b/jdk/test/javax/sound/sampled/spi/AudioFileWriter/ExpectedNPEOnNull.java new file mode 100644 index 00000000000..1e128444235 --- /dev/null +++ b/jdk/test/javax/sound/sampled/spi/AudioFileWriter/ExpectedNPEOnNull.java @@ -0,0 +1,310 @@ +/* + * 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.io.ByteArrayInputStream; +import java.io.File; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.spi.AudioFileWriter; + +import static java.util.ServiceLoader.load; +import static javax.sound.sampled.AudioFileFormat.Type; +import static javax.sound.sampled.AudioFormat.*; + +/** + * @test + * @bug 8135100 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + /** + * We will try to use all encoding, in this case all our providers will be + * covered by supported/unsupported encoding. + */ + private static final Encoding[] encodings = {Encoding.PCM_SIGNED, + Encoding.PCM_UNSIGNED, + Encoding.PCM_FLOAT, + Encoding.ULAW, Encoding.ALAW, + new Encoding("test")}; + + /** + * We will try to use all types, in this case all our providers will be + * covered by supported/unsupported types. + */ + private static final Type[] types = {Type.WAVE, Type.AU, Type.AIFF, + Type.AIFC, Type.SND, + new Type("MIDI", "mid"), + new Type("test", "test")}; + + /** + * We will try to use all supported AudioInputStream, in this case all our + * providers will be covered by supported/unsupported streams. + */ + private static final List aiss = new ArrayList<>(); + + static { + for (final Encoding encoding : encodings) { + for (final Type type : types) { + aiss.add(getAIS(type, encoding)); + } + } + } + + public static void main(final String[] args) throws Exception { + testAS(); + for (final AudioFileWriter afw : load(AudioFileWriter.class)) { + testAFW(afw); + } + testAFW(customAFW); + } + + /** + * Tests the part of AudioSystem API, which implemented via AudioFileWriter. + */ + private static void testAS() throws Exception { + + // AudioSystem#getAudioFileTypes(AudioInputStream) + try { + AudioSystem.getAudioFileTypes(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + + // AudioSystem#isFileTypeSupported(Type) + try { + AudioSystem.isFileTypeSupported(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + + // AudioSystem#isFileTypeSupported(Type, AudioInputStream) + for (final Type type : types) { + try { + AudioSystem.isFileTypeSupported(type, null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + try { + AudioSystem.isFileTypeSupported(null, stream); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + // AudioSystem#write(AudioInputStream, Type, OutputStream) + for (final Type type : types) { + try { + AudioSystem.write(null, type, new NullOutputStream()); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + try { + AudioSystem.write(stream, null, new NullOutputStream()); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final Type type : types) { + for (final AudioInputStream stream : aiss) { + try { + AudioSystem.write(stream, type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + } + + // AudioSystem#write(AudioInputStream, Type, File) + for (final Type type : types) { + try { + AudioSystem.write(null, type, new File("test.sound")); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + for (final AudioInputStream stream : aiss) { + try { + AudioSystem.write(stream, null, new File("test.sound")); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + for (final Type type : types) { + try { + AudioSystem.write(stream, type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + } + } + + /** + * Tests the AudioFileWriter API directly. + */ + private static void testAFW(final AudioFileWriter afw) throws Exception { + + // AudioFileWriter#isFileTypeSupported(Type) + try { + afw.isFileTypeSupported(null); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + + // AudioFileWriter#getAudioFileTypes(AudioInputStream) + try { + afw.getAudioFileTypes(null); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + + // AudioFileWriter#isFileTypeSupported(Type, AudioInputStream) + for (final Type type : types) { + try { + afw.isFileTypeSupported(type, null); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + try { + afw.isFileTypeSupported(null, stream); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + } + + // AudioFileWriter#write(AudioInputStream, Type, OutputStream) + for (final Type type : types) { + try { + afw.write(null, type, new NullOutputStream()); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + try { + afw.write(stream, null, new NullOutputStream()); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + for (final Type type : types) { + try { + afw.write(stream, type, (OutputStream) null); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + } + } + + // AudioFileWriter#write(AudioInputStream, Type, File) + for (final Type type : types) { + try { + afw.write(null, type, new File("test.sound")); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + try { + afw.write(stream, null, new File("test.sound")); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + for (final Type type : types) { + try { + afw.write(stream, type, (File) null); + throw new RuntimeException("NPE is expected: " + afw); + } catch (final NullPointerException ignored) { + } + } + } + } + + /** + * Tests some default implementation of AudioFileWriter API, using the + * custom {@code AudioFileWriter}, which support nothing. + */ + static final AudioFileWriter customAFW = new AudioFileWriter() { + @Override + public Type[] getAudioFileTypes() { + return new Type[0]; + } + + @Override + public Type[] getAudioFileTypes(final AudioInputStream stream) { + Objects.requireNonNull(stream); + return new Type[0]; + } + + @Override + public int write(AudioInputStream stream, Type fileType, + OutputStream out) { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); + return 0; + } + + @Override + public int write(AudioInputStream stream, Type fileType, File out) { + Objects.requireNonNull(stream); + Objects.requireNonNull(fileType); + Objects.requireNonNull(out); + return 0; + } + }; + + private static AudioInputStream getAIS(final Type type, Encoding encoding) { + AudioFormat af = new AudioFormat(encoding, 44100.0f, 16, 2, 1, 1, true); + AudioFileFormat aif = new AudioFileFormat(type, af, 0); + ByteArrayInputStream bais = new ByteArrayInputStream(new byte[1024]); + return new AudioInputStream(bais, aif.getFormat(), 0); + } + + private static final class NullOutputStream extends OutputStream { + + @Override + public void write(final int b) { + //do nothing + } + } +} diff --git a/jdk/test/javax/sound/sampled/FileWriter/WriterCloseInput.java b/jdk/test/javax/sound/sampled/spi/AudioFileWriter/WriterCloseInput.java similarity index 100% rename from jdk/test/javax/sound/sampled/FileWriter/WriterCloseInput.java rename to jdk/test/javax/sound/sampled/spi/AudioFileWriter/WriterCloseInput.java diff --git a/jdk/test/javax/sound/sampled/spi/FormatConversionProvider/ExpectedNPEOnNull.java b/jdk/test/javax/sound/sampled/spi/FormatConversionProvider/ExpectedNPEOnNull.java new file mode 100644 index 00000000000..ce7125c7320 --- /dev/null +++ b/jdk/test/javax/sound/sampled/spi/FormatConversionProvider/ExpectedNPEOnNull.java @@ -0,0 +1,356 @@ +/* + * 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.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioFormat.Encoding; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.spi.FormatConversionProvider; + +import static java.util.ServiceLoader.load; +import static javax.sound.sampled.AudioFileFormat.*; + +/** + * @test + * @bug 8135100 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + /** + * We will try to use all encoding, in this case all our providers will be + * covered by supported/unsupported encoding. + */ + private static final Encoding[] encodings = {Encoding.PCM_SIGNED, + Encoding.PCM_UNSIGNED, + Encoding.PCM_FLOAT, + Encoding.ULAW, Encoding.ALAW, + new Encoding("test")}; + + /** + * We will try to use all types, in this case all our providers will be + * covered by supported/unsupported types. + */ + private static final Type[] types = {Type.WAVE, Type.AU, Type.AIFF, + Type.AIFC, Type.SND, + new Type("MIDI", "mid"), + new Type("test", "test")}; + + /** + * We will try to use all supported AudioInputStream, in this case all our + * providers will be covered by supported/unsupported streams. + */ + private static final List aiss = new ArrayList<>(); + + static { + for (final Encoding encoding : encodings) { + for (final Type type : types) { + aiss.add(getAIS(type, encoding)); + } + } + } + + public static void main(final String[] args) throws Exception { + testAS(); + for (final FormatConversionProvider fcp : load( + FormatConversionProvider.class)) { + testFCP(fcp); + } + testFCP(customFCP); + } + + /** + * Tests the part of AudioSystem API, which implemented via + * FormatConversionProvider. + */ + private static void testAS() throws Exception { + + // AudioSystem#getAudioInputStream(Encoding, AudioInputStream) + for (final Encoding encoding : encodings) { + try { + AudioSystem.getAudioInputStream(encoding, null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final AudioInputStream stream : aiss) { + try { + AudioSystem.getAudioInputStream((Encoding) null, stream); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // AudioSystem#getAudioInputStream(AudioFormat, AudioInputStream) + for (final AudioInputStream stream : aiss) { + try { + AudioSystem.getAudioInputStream((AudioFormat) null, stream); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + AudioSystem.getAudioInputStream(getAFF(encoding), null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + // AudioSystem#getTargetEncodings(AudioFormat) + try { + AudioSystem.getTargetEncodings((AudioFormat) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + + // AudioSystem#getTargetEncodings(AudioFormat.Encoding) + try { + AudioSystem.getTargetEncodings((Encoding) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + + // AudioSystem#getTargetFormats(AudioFormat.Encoding, AudioFormat) + for (final Encoding encoding : encodings) { + try { + AudioSystem.getTargetFormats(null, getAFF(encoding)); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + AudioSystem.getTargetFormats(encoding, null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + // AudioSystem#isConversionSupported(AudioFormat, AudioFormat) + for (final Encoding encoding : encodings) { + try { + AudioSystem.isConversionSupported((AudioFormat) null, + getAFF(encoding)); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + AudioSystem.isConversionSupported(getAFF(encoding), null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + // AudioSystem#isConversionSupported(AudioFormat.Encoding, AudioFormat) + for (final Encoding encoding : encodings) { + try { + AudioSystem.isConversionSupported((Encoding) null, + getAFF(encoding)); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + AudioSystem.isConversionSupported(encoding, null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + } + + /** + * Tests the FormatConversionProvider API directly. + */ + private static void testFCP(FormatConversionProvider fcp) throws Exception { + + // FormatConversionProvider#isSourceEncodingSupported(Encoding) + try { + fcp.isSourceEncodingSupported(null); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + + // FormatConversionProvider#isTargetEncodingSupported(Encoding) + try { + fcp.isTargetEncodingSupported(null); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + + // FormatConversionProvider#getTargetEncodings() + try { + fcp.getTargetEncodings(null); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + + // FormatConversionProvider#isConversionSupported(Encoding, AudioFormat) + for (final Encoding encoding : encodings) { + try { + fcp.isConversionSupported((Encoding) null, getAFF(encoding)); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + fcp.isConversionSupported(encoding, null); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + + // FormatConversionProvider#getTargetFormats(Encoding, AudioFormat) + for (final Encoding encoding : encodings) { + try { + fcp.getTargetFormats(null, getAFF(encoding)); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + fcp.getTargetFormats(encoding, null); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + + // FormatConversionProvider#isConversionSupported(AudioFormat, + // AudioFormat) + for (final Encoding encoding : encodings) { + try { + fcp.isConversionSupported((AudioFormat) null, getAFF(encoding)); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + fcp.isConversionSupported(getAFF(encoding), null); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + + // FormatConversionProvider#getAudioInputStream(Encoding, + // AudioInputStream) + for (final AudioInputStream stream : aiss) { + try { + fcp.getAudioInputStream((Encoding) null, stream); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + fcp.getAudioInputStream(encoding, null); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + + // FormatConversionProvider#getAudioInputStream(AudioFormat, + // AudioInputStream) + for (final AudioInputStream stream : aiss) { + try { + fcp.getAudioInputStream((AudioFormat) null, stream); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + for (final Encoding encoding : encodings) { + try { + fcp.getAudioInputStream(getAFF(encoding), null); + throw new RuntimeException("NPE is expected: " + fcp); + } catch (final NullPointerException ignored) { + } + } + } + + /** + * Tests some default implementation of FormatConversionProvider API, using + * the custom {@code FormatConversionProvider}, which support nothing. + */ + static FormatConversionProvider customFCP = new FormatConversionProvider() { + + @Override + public Encoding[] getSourceEncodings() { + return new Encoding[0]; + } + + @Override + public Encoding[] getTargetEncodings() { + return new Encoding[0]; + } + + @Override + public Encoding[] getTargetEncodings(AudioFormat sourceFormat) { + Objects.requireNonNull(sourceFormat); + return new Encoding[0]; + } + + @Override + public AudioFormat[] getTargetFormats(Encoding enc, AudioFormat frmt) { + Objects.requireNonNull(enc); + Objects.requireNonNull(frmt); + return new AudioFormat[0]; + } + + @Override + public AudioInputStream getAudioInputStream(Encoding encoding, + AudioInputStream stream) { + Objects.requireNonNull(encoding); + Objects.requireNonNull(stream); + return null; + } + + @Override + public AudioInputStream getAudioInputStream(AudioFormat format, + AudioInputStream stream) { + Objects.requireNonNull(format); + Objects.requireNonNull(stream); + return null; + } + }; + + private static AudioFormat getAFF(final Encoding encoding) { + return new AudioFormat(encoding, 44100.0f, 16, 2, 1, 1, true); + } + + private static AudioInputStream getAIS(final Type type, Encoding encoding) { + AudioFormat af = new AudioFormat(encoding, 44100.0f, 16, 2, 1, 1, true); + AudioFileFormat aif = new AudioFileFormat(type, af, 0); + ByteArrayInputStream bais = new ByteArrayInputStream(new byte[1024]); + return new AudioInputStream(bais, aif.getFormat(), 0); + } +} diff --git a/jdk/test/javax/sound/sampled/spi/MixerProvider/ExpectedNPEOnNull.java b/jdk/test/javax/sound/sampled/spi/MixerProvider/ExpectedNPEOnNull.java new file mode 100644 index 00000000000..8f88472bd2c --- /dev/null +++ b/jdk/test/javax/sound/sampled/spi/MixerProvider/ExpectedNPEOnNull.java @@ -0,0 +1,88 @@ +/* + * 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 javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.spi.MixerProvider; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8135100 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testAS(); + for (final MixerProvider mp : load(MixerProvider.class)) { + testMP(mp); + } + testMP(customMP); + } + + /** + * Tests the part of AudioSystem API, which implemented via MixerProvider. + */ + private static void testAS() { + try { + AudioSystem.getMixer(null); // null should be accepted + } catch (final SecurityException | IllegalArgumentException ignored) { + // skip the specified exceptions only + } + } + + /** + * Tests the MixerProvider API directly. + */ + private static void testMP(MixerProvider mp) { + try { + mp.isMixerSupported(null); + throw new RuntimeException("NPE is expected: " + mp); + } catch (final NullPointerException ignored) { + + } + try { + mp.getMixer(null); // null should be accepted + } catch (SecurityException | IllegalArgumentException e) { + // skip the specified exceptions only + } + } + + /** + * Tests some default implementation of MixerProvider API, using the + * custom {@code MixerProvider}, which support nothing. + */ + static final MixerProvider customMP = new MixerProvider() { + @Override + public Mixer.Info[] getMixerInfo() { + return new Mixer.Info[0]; + } + + @Override + public Mixer getMixer(Mixer.Info info) { + return null; + } + }; +} From dad26e258cae0bbeab893b90b6eb552065f8da30 Mon Sep 17 00:00:00 2001 From: Amy Lu Date: Mon, 23 Nov 2015 16:14:33 +0800 Subject: [PATCH 047/199] 8143583: Several tests don't work with latest jtreg due to non-existing files in @build Reviewed-by: alanb, sla --- jdk/test/com/sun/jdi/DoubleAgentTest.java | 2 +- jdk/test/com/sun/jdi/SuspendNoFlagTest.java | 2 +- .../com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.java | 4 ++-- jdk/test/sun/tools/jmap/BasicJMapTest.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/jdk/test/com/sun/jdi/DoubleAgentTest.java b/jdk/test/com/sun/jdi/DoubleAgentTest.java index 91babd72e26..be3b8d8ffdc 100644 --- a/jdk/test/com/sun/jdi/DoubleAgentTest.java +++ b/jdk/test/com/sun/jdi/DoubleAgentTest.java @@ -31,7 +31,7 @@ import jdk.testlibrary.Utils; * * @library /lib/testlibrary * @modules java.management - * @build jdk.testlibarary.* + * @build jdk.testlibrary.* * @build DoubleAgentTest Exit0 * @run driver DoubleAgentTest */ diff --git a/jdk/test/com/sun/jdi/SuspendNoFlagTest.java b/jdk/test/com/sun/jdi/SuspendNoFlagTest.java index 8f91db2bac5..346dcca8923 100644 --- a/jdk/test/com/sun/jdi/SuspendNoFlagTest.java +++ b/jdk/test/com/sun/jdi/SuspendNoFlagTest.java @@ -29,7 +29,7 @@ import jdk.testlibrary.ProcessTools; * @summary Test for JDWP: -agentlib:jdwp=suspend=n hanging * @library /lib/testlibrary * @modules java.management - * @build jdk.testlibarary.* + * @build jdk.testlibrary.* * @compile -g HelloWorld.java * @run driver SuspendNoFlagTest */ diff --git a/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.java b/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.java index 1a64ae0d0a5..8a2c224f461 100644 --- a/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.java +++ b/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.java @@ -41,9 +41,9 @@ import com.sun.management.HotSpotDiagnosticMXBean; * @library /test/lib/share/classes * @build jdk.testlibrary.* * @build jdk.test.lib.hprof.* - * @build jdk.test.lib.hprof.module.* + * @build jdk.test.lib.hprof.model.* * @build jdk.test.lib.hprof.parser.* - * @build jdk.test.lib.hprof.utils.* + * @build jdk.test.lib.hprof.util.* * @run main DumpHeap */ public class DumpHeap { diff --git a/jdk/test/sun/tools/jmap/BasicJMapTest.java b/jdk/test/sun/tools/jmap/BasicJMapTest.java index 03efac77cf0..1ddb903a099 100644 --- a/jdk/test/sun/tools/jmap/BasicJMapTest.java +++ b/jdk/test/sun/tools/jmap/BasicJMapTest.java @@ -42,9 +42,9 @@ import jdk.testlibrary.ProcessTools; * @modules java.management * @build jdk.testlibrary.* * @build jdk.test.lib.hprof.* - * @build jdk.test.lib.hprof.module.* + * @build jdk.test.lib.hprof.model.* * @build jdk.test.lib.hprof.parser.* - * @build jdk.test.lib.hprof.utils.* + * @build jdk.test.lib.hprof.util.* * @run main/timeout=240 BasicJMapTest */ public class BasicJMapTest { From 4e828a68b5846a2f669b1355489d437c14a4d28c Mon Sep 17 00:00:00 2001 From: Vikrant Agarwal Date: Mon, 23 Nov 2015 14:44:41 +0300 Subject: [PATCH 048/199] 7146533: [TEST BUG] [macosx] skip java/awt/xembed/server/RunTestXEmbed.java for Mac OS X Reviewed-by: alexsch, serb --- jdk/test/java/awt/xembed/server/RunTestXEmbed.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jdk/test/java/awt/xembed/server/RunTestXEmbed.java b/jdk/test/java/awt/xembed/server/RunTestXEmbed.java index 58f8ecfce3f..04bb05dc04f 100644 --- a/jdk/test/java/awt/xembed/server/RunTestXEmbed.java +++ b/jdk/test/java/awt/xembed/server/RunTestXEmbed.java @@ -23,9 +23,12 @@ /** * @test - * @bug 4931668 + * @bug 4931668 7146533 * @summary Tests XEmbed server/client functionality * @author Denis Mikhalkin: area=awt.xembed + * @requires (!(os.family=="mac") & !(os.family=="windows")) + * @library /lib/testlibrary + * @build jdk.testlibrary.Platform * @modules java.desktop/sun.awt * @compile JavaClient.java TesterClient.java TestXEmbedServer.java * @run main/timeout=6000 RunTestXEmbed @@ -93,7 +96,7 @@ public class RunTestXEmbed extends TestXEmbedServer { } public static void main(String[] args) throws Throwable { - if (System.getProperty("os.name").toLowerCase().startsWith("win")) { + if (Platform.isWindows() || Platform.isOSX()) { return; } From f22e763e60282213fda27a7171f5aaf0a23bff7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Mon, 23 Nov 2015 15:26:10 +0100 Subject: [PATCH 049/199] 8141407: Wrong evaluation of a != a when a = NaN Reviewed-by: sundar, attila --- .../internal/runtime/ScriptRuntime.java | 4 +- nashorn/test/script/basic/JDK-8141407.js | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 nashorn/test/script/basic/JDK-8141407.js diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java index 23e595b8b2b..2d47d78ee34 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java @@ -756,7 +756,9 @@ public final class ScriptRuntime { /** ECMA 11.9.3 The Abstract Equality Comparison Algorithm */ private static boolean equals(final Object x, final Object y) { - if (x == y) { + // We want to keep this method small so we skip reference equality check for numbers + // as NaN should return false when compared to itself (JDK-8043608). + if (x == y && !(x instanceof Number)) { return true; } if (x instanceof ScriptObject && y instanceof ScriptObject) { diff --git a/nashorn/test/script/basic/JDK-8141407.js b/nashorn/test/script/basic/JDK-8141407.js new file mode 100644 index 00000000000..b801bfdaf78 --- /dev/null +++ b/nashorn/test/script/basic/JDK-8141407.js @@ -0,0 +1,62 @@ +/* + * 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. + */ + +/** + * JDK-8141407: Wrong evaluation of a != a when a = NaN + * + * @test + * @run + */ + +function expectNotEqualToSelf(a) { + Assert.assertFalse(a == a); + Assert.assertFalse(a === a); + Assert.assertTrue(a != a); + Assert.assertTrue(a !== a); +} + +// In previous versions of Nashorn this failed only on the second assignment, +// because only then the property slot was widened to object. +var a = NaN; +expectNotEqualToSelf(a); +a = {}; +a = NaN; +expectNotEqualToSelf(a); + +// We do have to do value-based rather than reference-based comparison for +// java.lang.Double since that class is used to represent primitive numbers +// in JavaScript. +var b = new java.lang.Double(NaN); +expectNotEqualToSelf(b); +b = {}; +b = new java.lang.Double(NaN); +expectNotEqualToSelf(b); + +// Although float is not used internally by Nashorn, java.lang.Float +// is handled like a primitive number in most of Nashorn, so for consistency +// we treat it like java.lang.Double. +var c = new java.lang.Float(NaN); +expectNotEqualToSelf(c); +c = {}; +c = new java.lang.Float(NaN); +expectNotEqualToSelf(c); From bcdeeca8008f72527bd307ea4971f1226c860339 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 23 Nov 2015 08:11:25 -0800 Subject: [PATCH 050/199] 8143813: Problem list PKCS8Test.java Reviewed-by: mullan --- jdk/test/ProblemList.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index cb33d6e7eae..d68562e043e 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -217,6 +217,9 @@ java/rmi/activation/Activatable/extLoadedImpl/ext.sh generic-all # jdk_security +# 8143377 +sun/security/pkcs/pkcs8/PKCS8Test.java solaris-all + # 7157786 sun/security/pkcs11/ec/TestKeyFactory.java generic-all From c51be36580091964d874e17c4a039f75d2046258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Mon, 23 Nov 2015 17:52:04 +0100 Subject: [PATCH 051/199] 8143821: Wrong test name in JDK-8143304 Reviewed-by: attila, sundar --- nashorn/test/script/basic/{JDK-8059934.js => JDK-8143304.js} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename nashorn/test/script/basic/{JDK-8059934.js => JDK-8143304.js} (95%) diff --git a/nashorn/test/script/basic/JDK-8059934.js b/nashorn/test/script/basic/JDK-8143304.js similarity index 95% rename from nashorn/test/script/basic/JDK-8059934.js rename to nashorn/test/script/basic/JDK-8143304.js index 42643b7dc3e..621077c3ca3 100644 --- a/nashorn/test/script/basic/JDK-8059934.js +++ b/nashorn/test/script/basic/JDK-8143304.js @@ -22,7 +22,7 @@ */ /** - * JDK-8059934: Random failures when script size exceeds token limits + * JDK-8143304: Random failures when script size exceeds token limits * * @test * @run From 301586f46277e6f6b1d0c14561ce72ee8d87d949 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 23 Nov 2015 09:58:44 -0800 Subject: [PATCH 052/199] 8143177: Integrate harfbuzz opentype layout engine per JEP 258 Reviewed-by: srl, vadim, serb --- jdk/make/lib/Awt2dLibraries.gmk | 32 +- jdk/make/mapfiles/libfontmanager/mapfile-vers | 1 + .../libfontmanager/mapfile-vers.openjdk | 1 + .../macosx/classes/sun/font/CFont.java | 7 + .../share/classes/sun/font/Font2D.java | 6 + .../share/classes/sun/font/GlyphLayout.java | 6 +- .../classes/sun/font/SunLayoutEngine.java | 49 +- .../share/classes/sun/font/TrueTypeFont.java | 1 + .../share/native/libfontmanager/HBShaper.c | 313 + .../harfbuzz/hb-atomic-private.hh | 164 + .../native/libfontmanager/harfbuzz/hb-blob.cc | 479 ++ .../native/libfontmanager/harfbuzz/hb-blob.h | 126 + .../harfbuzz/hb-buffer-deserialize-json.hh | 643 +++ .../harfbuzz/hb-buffer-deserialize-text.hh | 571 ++ .../harfbuzz/hb-buffer-private.hh | 220 + .../harfbuzz/hb-buffer-serialize.cc | 418 ++ .../libfontmanager/harfbuzz/hb-buffer.cc | 1701 ++++++ .../libfontmanager/harfbuzz/hb-buffer.h | 378 ++ .../harfbuzz/hb-cache-private.hh | 74 + .../libfontmanager/harfbuzz/hb-common.cc | 593 ++ .../libfontmanager/harfbuzz/hb-common.h | 354 ++ .../libfontmanager/harfbuzz/hb-coretext.cc | 1219 ++++ .../libfontmanager/harfbuzz/hb-coretext.h | 60 + .../libfontmanager/harfbuzz/hb-deprecated.h | 51 + .../harfbuzz/hb-face-private.hh | 107 + .../native/libfontmanager/harfbuzz/hb-face.cc | 481 ++ .../native/libfontmanager/harfbuzz/hb-face.h | 117 + .../harfbuzz/hb-fallback-shape.cc | 141 + .../harfbuzz/hb-font-private.hh | 415 ++ .../native/libfontmanager/harfbuzz/hb-font.cc | 1266 +++++ .../native/libfontmanager/harfbuzz/hb-font.h | 512 ++ .../native/libfontmanager/harfbuzz/hb-ft.cc | 650 +++ .../native/libfontmanager/harfbuzz/hb-ft.h | 126 + .../harfbuzz/hb-mutex-private.hh | 141 + .../harfbuzz/hb-object-private.hh | 202 + .../harfbuzz/hb-open-file-private.hh | 268 + .../harfbuzz/hb-open-type-private.hh | 1046 ++++ .../harfbuzz/hb-ot-cmap-table.hh | 528 ++ .../libfontmanager/harfbuzz/hb-ot-font.cc | 434 ++ .../libfontmanager/harfbuzz/hb-ot-font.h | 45 + .../harfbuzz/hb-ot-glyf-table.hh | 104 + .../harfbuzz/hb-ot-head-table.hh | 152 + .../harfbuzz/hb-ot-hhea-table.hh | 103 + .../harfbuzz/hb-ot-hmtx-table.hh | 104 + .../harfbuzz/hb-ot-layout-common-private.hh | 1224 ++++ .../harfbuzz/hb-ot-layout-gdef-table.hh | 443 ++ .../harfbuzz/hb-ot-layout-gpos-table.hh | 1643 ++++++ .../harfbuzz/hb-ot-layout-gsub-table.hh | 1342 +++++ .../harfbuzz/hb-ot-layout-gsubgpos-private.hh | 2286 ++++++++ .../harfbuzz/hb-ot-layout-jstf-table.hh | 234 + .../harfbuzz/hb-ot-layout-private.hh | 507 ++ .../libfontmanager/harfbuzz/hb-ot-layout.cc | 1058 ++++ .../libfontmanager/harfbuzz/hb-ot-layout.h | 302 + .../harfbuzz/hb-ot-map-private.hh | 248 + .../libfontmanager/harfbuzz/hb-ot-map.cc | 315 + .../harfbuzz/hb-ot-maxp-table.hh | 72 + .../harfbuzz/hb-ot-name-table.hh | 136 + .../hb-ot-shape-complex-arabic-fallback.hh | 354 ++ .../hb-ot-shape-complex-arabic-private.hh | 50 + .../hb-ot-shape-complex-arabic-table.hh | 383 ++ .../hb-ot-shape-complex-arabic-win1256.hh | 323 ++ .../harfbuzz/hb-ot-shape-complex-arabic.cc | 389 ++ .../harfbuzz/hb-ot-shape-complex-default.cc | 44 + .../harfbuzz/hb-ot-shape-complex-hangul.cc | 423 ++ .../harfbuzz/hb-ot-shape-complex-hebrew.cc | 172 + .../hb-ot-shape-complex-indic-machine.hh | 1762 ++++++ .../hb-ot-shape-complex-indic-private.hh | 180 + .../hb-ot-shape-complex-indic-table.cc | 956 ++++ .../harfbuzz/hb-ot-shape-complex-indic.cc | 1843 ++++++ .../hb-ot-shape-complex-myanmar-machine.hh | 400 ++ .../harfbuzz/hb-ot-shape-complex-myanmar.cc | 532 ++ .../harfbuzz/hb-ot-shape-complex-private.hh | 355 ++ .../harfbuzz/hb-ot-shape-complex-thai.cc | 381 ++ .../harfbuzz/hb-ot-shape-complex-tibetan.cc | 61 + .../hb-ot-shape-complex-use-machine.hh | 548 ++ .../hb-ot-shape-complex-use-private.hh | 97 + .../harfbuzz/hb-ot-shape-complex-use-table.cc | 696 +++ .../harfbuzz/hb-ot-shape-complex-use.cc | 585 ++ .../harfbuzz/hb-ot-shape-fallback-private.hh | 49 + .../harfbuzz/hb-ot-shape-fallback.cc | 484 ++ .../harfbuzz/hb-ot-shape-normalize-private.hh | 69 + .../harfbuzz/hb-ot-shape-normalize.cc | 416 ++ .../harfbuzz/hb-ot-shape-private.hh | 104 + .../libfontmanager/harfbuzz/hb-ot-shape.cc | 892 +++ .../libfontmanager/harfbuzz/hb-ot-shape.h | 53 + .../libfontmanager/harfbuzz/hb-ot-tag.cc | 931 +++ .../libfontmanager/harfbuzz/hb-ot-tag.h | 59 + .../native/libfontmanager/harfbuzz/hb-ot.h | 43 + .../libfontmanager/harfbuzz/hb-private.hh | 953 ++++ .../libfontmanager/harfbuzz/hb-set-private.hh | 402 ++ .../native/libfontmanager/harfbuzz/hb-set.cc | 471 ++ .../native/libfontmanager/harfbuzz/hb-set.h | 157 + .../harfbuzz/hb-shape-plan-private.hh | 62 + .../libfontmanager/harfbuzz/hb-shape-plan.cc | 500 ++ .../libfontmanager/harfbuzz/hb-shape-plan.h | 89 + .../libfontmanager/harfbuzz/hb-shape.cc | 406 ++ .../native/libfontmanager/harfbuzz/hb-shape.h | 78 + .../harfbuzz/hb-shaper-impl-private.hh | 43 + .../libfontmanager/harfbuzz/hb-shaper-list.hh | 55 + .../harfbuzz/hb-shaper-private.hh | 108 + .../libfontmanager/harfbuzz/hb-shaper.cc | 111 + .../native/libfontmanager/harfbuzz/hb-ucdn.cc | 237 + .../libfontmanager/harfbuzz/hb-ucdn/ucdn.c | 281 + .../libfontmanager/harfbuzz/hb-ucdn/ucdn.h | 364 ++ .../harfbuzz/hb-ucdn/unicodedata_db.h | 5062 +++++++++++++++++ .../harfbuzz/hb-unicode-private.hh | 317 ++ .../libfontmanager/harfbuzz/hb-unicode.cc | 563 ++ .../libfontmanager/harfbuzz/hb-unicode.h | 470 ++ .../libfontmanager/harfbuzz/hb-utf-private.hh | 278 + .../libfontmanager/harfbuzz/hb-version.h | 66 + .../libfontmanager/harfbuzz/hb-warning.cc | 39 + .../share/native/libfontmanager/harfbuzz/hb.h | 47 + .../native/libfontmanager/hb-jdk-font.cc | 365 ++ .../share/native/libfontmanager/hb-jdk.h | 74 + .../native/libfontmanager/scriptMapping.c | 92 + .../native/libfontmanager/scriptMapping.h | 33 + .../awt/font/TextLayout/TestLayoutVsICU.java | 889 +++ .../TextLayout/TestLayoutVsICU_jdkbase.xml | 1827 ++++++ 118 files changed, 52287 insertions(+), 5 deletions(-) create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-atomic-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-blob.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-blob.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-deserialize-json.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-deserialize-text.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-serialize.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-cache-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-coretext.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-coretext.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-deprecated.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-fallback-shape.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ft.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ft.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-mutex-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-object-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-open-file-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-open-type-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-cmap-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-font.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-font.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-glyf-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-head-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-hhea-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-hmtx-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-common-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-gdef-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-gpos-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-gsub-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-gsubgpos-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-jstf-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-map-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-map.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-maxp-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-name-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-arabic-fallback.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-arabic-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-arabic-table.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-arabic-win1256.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-arabic.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-default.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-hangul.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-hebrew.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-indic-machine.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-indic-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-indic-table.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-indic.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-myanmar-machine.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-myanmar.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-thai.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-tibetan.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-use-machine.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-use-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-use-table.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-use.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-fallback-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-fallback.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-normalize-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-normalize.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-tag.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-tag.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-set-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-set.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-set.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shape-plan-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shape-plan.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shape-plan.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shape.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shape.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shaper-impl-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shaper-list.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shaper-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-shaper.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ucdn.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ucdn/ucdn.c create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ucdn/ucdn.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ucdn/unicodedata_db.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-unicode-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-unicode.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-unicode.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-utf-private.hh create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-version.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-warning.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/hb-jdk.h create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/scriptMapping.c create mode 100644 jdk/src/java.desktop/share/native/libfontmanager/scriptMapping.h create mode 100644 jdk/test/java/awt/font/TextLayout/TestLayoutVsICU.java create mode 100644 jdk/test/java/awt/font/TextLayout/TestLayoutVsICU_jdkbase.xml diff --git a/jdk/make/lib/Awt2dLibraries.gmk b/jdk/make/lib/Awt2dLibraries.gmk index 21e61c5c679..bc4a95064a3 100644 --- a/jdk/make/lib/Awt2dLibraries.gmk +++ b/jdk/make/lib/Awt2dLibraries.gmk @@ -602,6 +602,34 @@ LIBFONTMANAGER_CFLAGS := \ $(LIBJAVA_HEADER_FLAGS) \ # +#### Begin harfbuzz configuration + +HARFBUZZ_CFLAGS := -DHAVE_OT -DHAVE_FALLBACK -DHAVE_UCDN + +ifneq ($(OPENJDK_TARGET_OS), windows) + HARFBUZZ_CFLAGS += -DGETPAGESIZE -DHAVE_MPROTECT -DHAVE_PTHREAD \ + -DHAVE_SYSCONF -DHAVE_SYS_MMAN_H -DHAVE_UNISTD_H +endif +ifneq (, $(findstring $(OPENJDK_TARGET_OS), linux macosx)) + HARFBUZZ_CFLAGS += -DHAVE_INTEL_ATOMIC_PRIMITIVES +endif +ifeq ($(OPENJDK_TARGET_OS), solaris) + HARFBUZZ_CFLAGS += -DHAVE_SOLARIS_ATOMIC_OPS +endif +ifeq ($(OPENJDK_TARGET_OS), macosx) + HARFBUZZ_CFLAGS += -DHAVE_CORETEXT +endif +ifneq ($(OPENJDK_TARGET_OS), macosx) + LIBFONTMANAGER_EXCLUDE_FILES += harfbuzz/hb-coretext.cc +endif +ifndef OPENJDK + LIBFONTMANAGER_EXCLUDE_FILES += harfbuzz/hb-ft.cc +endif + +LIBFONTMANAGER_CFLAGS += $(HARFBUZZ_CFLAGS) + +#### End harfbuzz configuration + ifndef OPENJDK LIBFONTMANAGER_CFLAGS += -I$(JDK_TOPDIR)/src/closed/java.desktop/share/native/libt2k BUILD_LIBFONTMANAGER_MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libfontmanager/mapfile-vers @@ -648,11 +676,11 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBFONTMANAGER, \ OPTIMIZATION := $(LIBFONTMANAGER_OPTIMIZATION), \ CFLAGS_windows = -DCC_NOEX, \ DISABLED_WARNINGS_gcc := sign-compare int-to-pointer-cast reorder \ - delete-non-virtual-dtor type-limits, \ + delete-non-virtual-dtor type-limits missing-field-initializers, \ DISABLED_WARNINGS_clang := unused-value incompatible-pointer-types \ tautological-constant-out-of-range-compare int-to-pointer-cast, \ DISABLED_WARNINGS_solstudio := truncwarn, \ - DISABLED_WARNINGS_microsoft := 4267 4244 4018 4090 4996 4146, \ + DISABLED_WARNINGS_microsoft := 4267 4244 4018 4090 4996 4146 4334, \ WARNINGS_AS_ERRORS_gcc := false, \ WARNINGS_AS_ERRORS_solstudio := false, \ MAPFILE := $(BUILD_LIBFONTMANAGER_MAPFILE), \ diff --git a/jdk/make/mapfiles/libfontmanager/mapfile-vers b/jdk/make/mapfiles/libfontmanager/mapfile-vers index 2a83f292644..f2acff6aa6d 100644 --- a/jdk/make/mapfiles/libfontmanager/mapfile-vers +++ b/jdk/make/mapfiles/libfontmanager/mapfile-vers @@ -41,6 +41,7 @@ SUNWprivate_1.1 { Java_sun_font_StrikeCache_freeLongMemory; Java_sun_font_SunLayoutEngine_initGVIDs; Java_sun_font_SunLayoutEngine_nativeLayout; + Java_sun_font_SunLayoutEngine_shape; Java_sun_font_X11TextRenderer_doDrawGlyphList; Java_sun_java2d_loops_DrawGlyphListAA_DrawGlyphListAA; Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD; diff --git a/jdk/make/mapfiles/libfontmanager/mapfile-vers.openjdk b/jdk/make/mapfiles/libfontmanager/mapfile-vers.openjdk index b20344eaaab..21ecea99119 100644 --- a/jdk/make/mapfiles/libfontmanager/mapfile-vers.openjdk +++ b/jdk/make/mapfiles/libfontmanager/mapfile-vers.openjdk @@ -43,6 +43,7 @@ SUNWprivate_1.1 { Java_sun_font_StrikeCache_freeLongMemory; Java_sun_font_SunLayoutEngine_initGVIDs; Java_sun_font_SunLayoutEngine_nativeLayout; + Java_sun_font_SunLayoutEngine_shape; Java_sun_font_X11TextRenderer_doDrawGlyphList; Java_sun_java2d_loops_DrawGlyphListAA_DrawGlyphListAA; Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD; diff --git a/jdk/src/java.desktop/macosx/classes/sun/font/CFont.java b/jdk/src/java.desktop/macosx/classes/sun/font/CFont.java index a25e1784811..dcc4b5793eb 100644 --- a/jdk/src/java.desktop/macosx/classes/sun/font/CFont.java +++ b/jdk/src/java.desktop/macosx/classes/sun/font/CFont.java @@ -198,6 +198,13 @@ public final class CFont extends PhysicalFont implements FontSubstitution { return nativeFontPtr; } + private native long getCGFontPtrNative(long ptr); + + // This digs the CGFont out of the AWTFont. + protected synchronized long getPlatformNativeFontPtr() { + return getCGFontPtrNative(getNativeFontPtr()); + } + static native void getCascadeList(long nativeFontPtr, ArrayList listOfString); private CompositeFont createCompositeFont() { diff --git a/jdk/src/java.desktop/share/classes/sun/font/Font2D.java b/jdk/src/java.desktop/share/classes/sun/font/Font2D.java index 7e7edc3e8cc..84fc658447f 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/Font2D.java +++ b/jdk/src/java.desktop/share/classes/sun/font/Font2D.java @@ -472,6 +472,12 @@ public abstract class Font2D { return 0L; } + /* Used only on OS X. + */ + protected long getPlatformNativeFontPtr() { + return 0L; + } + /* for layout code */ protected long getUnitsPerEm() { return 2048; diff --git a/jdk/src/java.desktop/share/classes/sun/font/GlyphLayout.java b/jdk/src/java.desktop/share/classes/sun/font/GlyphLayout.java index 143795e7b6a..0363f0f43fa 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/GlyphLayout.java +++ b/jdk/src/java.desktop/share/classes/sun/font/GlyphLayout.java @@ -96,6 +96,7 @@ public final class GlyphLayout { private Point2D.Float _pt; private FontStrikeDesc _sd; private float[] _mat; + private float ptSize; private int _typo_flags; private int _offset; @@ -172,7 +173,7 @@ public final class GlyphLayout { * If the GVData does not have room for the glyphs, throws an IndexOutOfBoundsException and * leave pt and the gvdata unchanged. */ - public void layout(FontStrikeDesc sd, float[] mat, int gmask, + public void layout(FontStrikeDesc sd, float[] mat, float ptSize, int gmask, int baseIndex, TextRecord text, int typo_flags, Point2D.Float pt, GVData data); } @@ -386,6 +387,7 @@ public final class GlyphLayout { _mat[2] = (float)txinfo.gtx.getShearX(); _mat[3] = (float)txinfo.gtx.getScaleY(); _pt.setLocation(txinfo.delta); + ptSize = font.getSize2D(); int lim = offset + count; @@ -682,7 +684,7 @@ public final class GlyphLayout { void layout() { _textRecord.start = start; _textRecord.limit = limit; - engine.layout(_sd, _mat, gmask, start - _offset, _textRecord, + engine.layout(_sd, _mat, ptSize, gmask, start - _offset, _textRecord, _typo_flags | eflags, _pt, _gvdata); } } diff --git a/jdk/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java b/jdk/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java index a62b1e5fe21..c7ab758c9a4 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java +++ b/jdk/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java @@ -103,9 +103,20 @@ import java.util.Locale; * */ public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory { private static native void initGVIDs(); + private static final boolean useICU; static { FontManagerNativeLibrary.load(); initGVIDs(); + String le = java.security.AccessController.doPrivileged( + new sun.security.action. + GetPropertyAction("sun.font.layoutengine", "")); + useICU = le.equals("icu"); + String verbose = java.security.AccessController.doPrivileged( + new sun.security.action. + GetPropertyAction("sun.font.layoutengine.verbose", "")); + if ("true".equalsIgnoreCase(verbose)) { + System.out.println("Using " + (useICU ? "icu." : "harfbuzz.")); + } } private LayoutEngineKey key; @@ -150,21 +161,57 @@ public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory this.key = key; } - public void layout(FontStrikeDesc desc, float[] mat, int gmask, + private boolean isAAT(Font2D font) { + if (font instanceof TrueTypeFont) { + TrueTypeFont ttf = (TrueTypeFont)font; + return ttf.getDirectoryEntry(TrueTypeFont.morxTag) != null || + ttf.getDirectoryEntry(TrueTypeFont.mortTag) != null; + } else if (font instanceof PhysicalFont) { + PhysicalFont pf = (PhysicalFont)font; + return pf.getTableBytes(TrueTypeFont.morxTag) != null || + pf.getTableBytes(TrueTypeFont.mortTag) != null; + } + return false; + } + + public void layout(FontStrikeDesc desc, float[] mat, float ptSize, int gmask, int baseIndex, TextRecord tr, int typo_flags, Point2D.Float pt, GVData data) { Font2D font = key.font(); FontStrike strike = font.getStrike(desc); long layoutTables = font.getLayoutTableCache(); + if (useICU) { nativeLayout(font, strike, mat, gmask, baseIndex, tr.text, tr.start, tr.limit, tr.min, tr.max, key.script(), key.lang(), typo_flags, pt, data, font.getUnitsPerEm(), layoutTables); + } else { + long pNativeFont = font.getPlatformNativeFontPtr(); // used on OSX + // pScaler probably not needed long term. + long pScaler = 0L; + if (font instanceof FileFont) { + pScaler = ((FileFont)font).getScaler().nativeScaler; + } + shape(font, strike, ptSize, mat, pScaler, pNativeFont, isAAT(font), + tr.text, data, key.script(), + tr.start, tr.limit, baseIndex, pt, + typo_flags, gmask); + } } + /* Native method to invoke ICU layout engine */ private static native void nativeLayout(Font2D font, FontStrike strike, float[] mat, int gmask, int baseIndex, char[] chars, int offset, int limit, int min, int max, int script, int lang, int typo_flags, Point2D.Float pt, GVData data, long upem, long layoutTables); + + + /* Native method to invoke harfbuzz layout engine */ + private static native boolean + shape(Font2D font, FontStrike strike, float ptSize, float[] mat, + long pscaler, long pNativeFont, boolean aat, + char[] chars, GVData data, + int script, int offset, int limit, + int baseIndex, Point2D.Float pt, int typo_flags, int slot); } diff --git a/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java b/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java index f3bb84529da..c4893b6dd60 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java +++ b/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java @@ -83,6 +83,7 @@ public class TrueTypeFont extends FileFont { public static final int GPOSTag = 0x47504F53; // 'GPOS' public static final int GSUBTag = 0x47535542; // 'GSUB' public static final int mortTag = 0x6D6F7274; // 'mort' + public static final int morxTag = 0x6D6F7278; // 'morx' /* -- Tags for non-standard tables */ public static final int fdscTag = 0x66647363; // 'fdsc' - gxFont descriptor diff --git a/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c b/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c new file mode 100644 index 00000000000..422575cb165 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c @@ -0,0 +1,313 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#include +#include +#include "hb.h" +#include "hb-jdk.h" +#include "hb-ot.h" +#ifdef MACOSX +#include "hb-coretext.h" +#endif +#include "scriptMapping.h" + +static jclass gvdClass = 0; +static const char* gvdClassName = "sun/font/GlyphLayout$GVData"; +static jfieldID gvdCountFID = 0; +static jfieldID gvdFlagsFID = 0; +static jfieldID gvdGlyphsFID = 0; +static jfieldID gvdPositionsFID = 0; +static jfieldID gvdIndicesFID = 0; +static int jniInited = 0; + +static void getFloat(JNIEnv* env, jobject pt, jfloat *x, jfloat *y) { + *x = (*env)->GetFloatField(env, pt, sunFontIDs.xFID); + *y = (*env)->GetFloatField(env, pt, sunFontIDs.yFID); +} + +static void putFloat(JNIEnv* env, jobject pt, jfloat x, jfloat y) { + (*env)->SetFloatField(env, pt, sunFontIDs.xFID, x); + (*env)->SetFloatField(env, pt, sunFontIDs.yFID, y); +} + +static int init_JNI_IDs(JNIEnv *env) { + if (jniInited) { + return jniInited; + } + CHECK_NULL_RETURN(gvdClass = (*env)->FindClass(env, gvdClassName), 0); + CHECK_NULL_RETURN(gvdClass = (jclass)(*env)->NewGlobalRef(env, gvdClass), 0); + CHECK_NULL_RETURN(gvdCountFID = (*env)->GetFieldID(env, gvdClass, "_count", "I"), 0); + CHECK_NULL_RETURN(gvdFlagsFID = (*env)->GetFieldID(env, gvdClass, "_flags", "I"), 0); + CHECK_NULL_RETURN(gvdGlyphsFID = (*env)->GetFieldID(env, gvdClass, "_glyphs", "[I"), 0); + CHECK_NULL_RETURN(gvdPositionsFID = (*env)->GetFieldID(env, gvdClass, "_positions", "[F"), 0); + CHECK_NULL_RETURN(gvdIndicesFID = (*env)->GetFieldID(env, gvdClass, "_indices", "[I"), 0); + jniInited = 1; + return jniInited; +} + +// gmask is the composite font slot mask +// baseindex is to be added to the character (code point) index. +int storeGVData(JNIEnv* env, + jobject gvdata, jint slot, jint baseIndex, jobject startPt, + int glyphCount, hb_glyph_info_t *glyphInfo, + hb_glyph_position_t *glyphPos, hb_direction_t direction) { + + int i; + float x=0, y=0; + float startX, startY; + float scale = 1.0f/64.0f; + unsigned int* glyphs; + float* positions; + + if (!init_JNI_IDs(env)) { + return 0; + } + + int initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID); + jarray glyphArray = + (jarray)(*env)->GetObjectField(env, gvdata, gvdGlyphsFID); + jarray posArray = + (jarray)(*env)->GetObjectField(env, gvdata, gvdPositionsFID); + + if (glyphArray == NULL || posArray == NULL) + { + JNU_ThrowArrayIndexOutOfBoundsException(env, ""); + return 0; + } + + // The Java code catches the IIOBE and expands the storage + // and re-invokes layout. I suppose this is expected to be rare + // because at least in a single threaded case there should be + // re-use of the same container, but it is a little wasteful/distateful. + int glyphArrayLen = (*env)->GetArrayLength(env, glyphArray); + int posArrayLen = (*env)->GetArrayLength(env, posArray); + int maxGlyphs = glyphCount + initialCount; + if ((maxGlyphs > glyphArrayLen) || + (maxGlyphs * 2 + 2 > posArrayLen)) + { + JNU_ThrowArrayIndexOutOfBoundsException(env, ""); + return 0; + } + + getFloat(env, startPt, &startX, &startY); + + glyphs = + (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, glyphArray, NULL); + positions = (jfloat*)(*env)->GetPrimitiveArrayCritical(env, posArray, NULL); + for (i = 0; i < glyphCount; i++) { + int storei = i + initialCount; + int index = glyphInfo[i].codepoint | slot; + if (iReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0); + (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0); + putFloat(env, startPt,positions[(storeadv*2)],positions[(storeadv*2)+1] ); + jarray inxArray = + (jarray)(*env)->GetObjectField(env, gvdata, gvdIndicesFID); + unsigned int* indices = + (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, inxArray, NULL); + int prevCluster = -1; + for (i = 0; i < glyphCount; i++) { + int cluster = glyphInfo[i].cluster; + if (direction == HB_DIRECTION_LTR) { + // I need to understand what hb does when processing a substring + // I expected the cluster index to be from the start of the text + // to process. + // Instead it appears to be from the start of the whole thing. + indices[i+initialCount] = cluster; + } else { + indices[i+initialCount] = baseIndex + glyphCount -1 -i; + } + } + (*env)->ReleasePrimitiveArrayCritical(env, inxArray, indices, 0); + (*env)->SetIntField(env, gvdata, gvdCountFID, initialCount+glyphCount); + return initialCount+glyphCount; +} + +static float euclidianDistance(float a, float b) +{ + float root; + if (a < 0) { + a = -a; + } + + if (b < 0) { + b = -b; + } + + if (a == 0) { + return b; + } + + if (b == 0) { + return a; + } + + /* Do an initial approximation, in root */ + root = a > b ? a + (b / 2) : b + (a / 2); + + /* An unrolled Newton-Raphson iteration sequence */ + root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; + root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; + root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; + + return root; +} + +JDKFontInfo* + createJDKFontInfo(JNIEnv *env, + jobject font2D, + jobject fontStrike, + jfloat ptSize, + jlong pScaler, + jlong pNativeFont, + jfloatArray matrix, + jboolean aat) { + + + JDKFontInfo *fi = (JDKFontInfo*)malloc(sizeof(JDKFontInfo)); + if (!fi) { + return NULL; + } + fi->env = env; // this is valid only for the life of this JNI call. + fi->font2D = font2D; + fi->fontStrike = fontStrike; + fi->nativeFont = pNativeFont; + fi->aat = aat; + (*env)->GetFloatArrayRegion(env, matrix, 0, 4, fi->matrix); + fi->ptSize = ptSize; + fi->xPtSize = euclidianDistance(fi->matrix[0], fi->matrix[1]); + fi->yPtSize = euclidianDistance(fi->matrix[2], fi->matrix[3]); + + return fi; +} + + +#define TYPO_RTL 0x80000000 + +JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape + (JNIEnv *env, jclass cls, + jobject font2D, + jobject fontStrike, + jfloat ptSize, + jfloatArray matrix, + jlong pScaler, + jlong pNativeFont, + jboolean aat, + jcharArray text, + jobject gvdata, + jint script, + jint offset, + jint limit, + jint baseIndex, + jobject startPt, + jint flags, + jint slot) { + + hb_buffer_t *buffer; + hb_font_t* hbfont; + jchar *chars; + jsize len; + int glyphCount; + hb_glyph_info_t *glyphInfo; + hb_glyph_position_t *glyphPos; + hb_direction_t direction = HB_DIRECTION_LTR; + hb_feature_t *features = NULL; + int featureCount = 0; + + int i; + unsigned int buflen; + + JDKFontInfo *jdkFontInfo = + createJDKFontInfo(env, font2D, fontStrike, ptSize, + pScaler, pNativeFont, matrix, aat); + if (!jdkFontInfo) { + return JNI_FALSE; + } + jdkFontInfo->env = env; // this is valid only for the life of this JNI call. + jdkFontInfo->font2D = font2D; + jdkFontInfo->fontStrike = fontStrike; + + hbfont = hb_jdk_font_create(jdkFontInfo, NULL); + + buffer = hb_buffer_create(); + hb_buffer_set_script(buffer, getHBScriptCode(script)); + hb_buffer_set_language(buffer, + hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE)); + if ((flags & TYPO_RTL) != 0) { + direction = HB_DIRECTION_RTL; + } + hb_buffer_set_direction(buffer, direction); + + chars = (*env)->GetCharArrayElements(env, text, NULL); + len = (*env)->GetArrayLength(env, text); + + hb_buffer_add_utf16(buffer, chars, len, offset, limit-offset); + + hb_shape_full(hbfont, buffer, features, featureCount, 0); + glyphCount = hb_buffer_get_length(buffer); + glyphInfo = hb_buffer_get_glyph_infos(buffer, 0); + glyphPos = hb_buffer_get_glyph_positions(buffer, &buflen); + for (i = 0; i < glyphCount; i++) { + int index = glyphInfo[i].codepoint; + int xadv = (glyphPos[i].x_advance); + int yadv = (glyphPos[i].y_advance); + } + // On "input" HB assigns a cluster index to each character in UTF-16. + // On output where a sequence of characters have been mapped to + // a glyph they are all mapped to the cluster index of the first character. + // The next cluster index will be that of the first character in the + // next cluster. So cluster indexes may 'skip' on output. + // This can also happen if there are supplementary code-points + // such that two UTF-16 characters are needed to make one codepoint. + // In RTL text you need to count down. + // So the following code tries to build the reverse map as expected + // by calling code. + + storeGVData(env, gvdata, slot, baseIndex, startPt, + glyphCount, glyphInfo, glyphPos, direction); + + hb_buffer_destroy (buffer); + hb_font_destroy(hbfont); + free((void*)jdkFontInfo); + if (features != NULL) free(features); + + return JNI_TRUE; +} + diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-atomic-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-atomic-private.hh new file mode 100644 index 00000000000..778e30bc2e8 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-atomic-private.hh @@ -0,0 +1,164 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ATOMIC_PRIVATE_HH +#define HB_ATOMIC_PRIVATE_HH + +#include "hb-private.hh" + + +/* atomic_int */ + +/* We need external help for these */ + +#if defined(hb_atomic_int_impl_add) \ + && defined(hb_atomic_ptr_impl_get) \ + && defined(hb_atomic_ptr_impl_cmpexch) + +/* Defined externally, i.e. in config.h; must have typedef'ed hb_atomic_int_impl_t as well. */ + + +#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) + +#include + +/* MinGW has a convoluted history of supporting MemoryBarrier + * properly. As such, define a function to wrap the whole + * thing. */ +static inline void _HBMemoryBarrier (void) { +#if !defined(MemoryBarrier) + long dummy = 0; + InterlockedExchange (&dummy, 1); +#else + MemoryBarrier (); +#endif +} + +typedef LONG hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) InterlockedExchangeAdd (&(AI), (V)) + +#define hb_atomic_ptr_impl_get(P) (_HBMemoryBarrier (), (void *) *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O)) + + +#elif !defined(HB_NO_MT) && defined(__APPLE__) + +#include +#ifdef __MAC_OS_X_MIN_REQUIRED +#include +#elif defined(__IPHONE_OS_MIN_REQUIRED) +#include +#endif + + +typedef int32_t hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V)) + +#define hb_atomic_ptr_impl_get(P) (OSMemoryBarrier (), (void *) *(P)) +#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P)) +#else +#if __ppc64__ || __x86_64__ || __aarch64__ +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P)) +#else +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P)) +#endif +#endif + + +#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) + +typedef int hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) __sync_fetch_and_add (&(AI), (V)) + +#define hb_atomic_ptr_impl_get(P) (void *) (__sync_synchronize (), *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N)) + + +#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS) + +#include +#include + +typedef unsigned int hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) ( ({__machine_rw_barrier ();}), atomic_add_int_nv (&(AI), (V)) - (V)) + +#define hb_atomic_ptr_impl_get(P) ( ({__machine_rw_barrier ();}), (void *) *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) ( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false) + + +#elif !defined(HB_NO_MT) + +#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */ + +typedef volatile int hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) (((AI) += (V)) - (V)) + +#define hb_atomic_ptr_impl_get(P) ((void *) *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false) + + +#else /* HB_NO_MT */ + +typedef int hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) (((AI) += (V)) - (V)) + +#define hb_atomic_ptr_impl_get(P) ((void *) *(P)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) + + +#endif + + +#define HB_ATOMIC_INT_INIT(V) {HB_ATOMIC_INT_IMPL_INIT(V)} + +struct hb_atomic_int_t +{ + hb_atomic_int_impl_t v; + + inline void set_unsafe (int v_) { v = v_; } + inline int get_unsafe (void) const { return v; } + inline int inc (void) { return hb_atomic_int_impl_add (const_cast (v), 1); } + inline int dec (void) { return hb_atomic_int_impl_add (const_cast (v), -1); } +}; + + +#define hb_atomic_ptr_get(P) hb_atomic_ptr_impl_get(P) +#define hb_atomic_ptr_cmpexch(P,O,N) hb_atomic_ptr_impl_cmpexch((P),(O),(N)) + + +#endif /* HB_ATOMIC_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-blob.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-blob.cc new file mode 100644 index 00000000000..40aa17f306c --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-blob.cc @@ -0,0 +1,479 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +/* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 199309L +#endif + +#include "hb-private.hh" + +#include "hb-object-private.hh" + +#ifdef HAVE_SYS_MMAN_H +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#endif /* HAVE_SYS_MMAN_H */ + +#include +#include + + + +#ifndef HB_DEBUG_BLOB +#define HB_DEBUG_BLOB (HB_DEBUG+0) +#endif + + +struct hb_blob_t { + hb_object_header_t header; + ASSERT_POD (); + + bool immutable; + + const char *data; + unsigned int length; + hb_memory_mode_t mode; + + void *user_data; + hb_destroy_func_t destroy; +}; + + +static bool _try_writable (hb_blob_t *blob); + +static void +_hb_blob_destroy_user_data (hb_blob_t *blob) +{ + if (blob->destroy) { + blob->destroy (blob->user_data); + blob->user_data = NULL; + blob->destroy = NULL; + } +} + +/** + * hb_blob_create: (skip) + * @data: Pointer to blob data. + * @length: Length of @data in bytes. + * @mode: Memory mode for @data. + * @user_data: Data parameter to pass to @destroy. + * @destroy: Callback to call when @data is not needed anymore. + * + * Creates a new "blob" object wrapping @data. The @mode parameter is used + * to negotiate ownership and lifecycle of @data. + * + * Return value: New blob, or the empty blob if something failed or if @length is + * zero. Destroy with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_blob_t *blob; + + if (!length || + length >= 1u << 31 || + data + length < data /* overflows */ || + !(blob = hb_object_create ())) { + if (destroy) + destroy (user_data); + return hb_blob_get_empty (); + } + + blob->data = data; + blob->length = length; + blob->mode = mode; + + blob->user_data = user_data; + blob->destroy = destroy; + + if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { + blob->mode = HB_MEMORY_MODE_READONLY; + if (!_try_writable (blob)) { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + return blob; +} + +/** + * hb_blob_create_sub_blob: + * @parent: Parent blob. + * @offset: Start offset of sub-blob within @parent, in bytes. + * @length: Length of sub-blob. + * + * Returns a blob that represents a range of bytes in @parent. The new + * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it + * will never modify data in the parent blob. The parent data is not + * expected to be modified, and will result in undefined behavior if it + * is. + * + * Makes @parent immutable. + * + * Return value: New blob, or the empty blob if something failed or if + * @length is zero or @offset is beyond the end of @parent's data. Destroy + * with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length) +{ + hb_blob_t *blob; + + if (!length || offset >= parent->length) + return hb_blob_get_empty (); + + hb_blob_make_immutable (parent); + + blob = hb_blob_create (parent->data + offset, + MIN (length, parent->length - offset), + HB_MEMORY_MODE_READONLY, + hb_blob_reference (parent), + (hb_destroy_func_t) hb_blob_destroy); + + return blob; +} + +/** + * hb_blob_get_empty: + * + * Returns the singleton empty blob. + * + * See TODO:link object types for more information. + * + * Return value: (transfer full): the empty blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_get_empty (void) +{ + static const hb_blob_t _hb_blob_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + NULL, /* data */ + 0, /* length */ + HB_MEMORY_MODE_READONLY, /* mode */ + + NULL, /* user_data */ + NULL /* destroy */ + }; + + return const_cast (&_hb_blob_nil); +} + +/** + * hb_blob_reference: (skip) + * @blob: a blob. + * + * Increases the reference count on @blob. + * + * See TODO:link object types for more information. + * + * Return value: @blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_reference (hb_blob_t *blob) +{ + return hb_object_reference (blob); +} + +/** + * hb_blob_destroy: (skip) + * @blob: a blob. + * + * Descreases the reference count on @blob, and if it reaches zero, destroys + * @blob, freeing all memory, possibly calling the destroy-callback the blob + * was created for if it has not been called already. + * + * See TODO:link object types for more information. + * + * Since: 0.9.2 + **/ +void +hb_blob_destroy (hb_blob_t *blob) +{ + if (!hb_object_destroy (blob)) return; + + _hb_blob_destroy_user_data (blob); + + free (blob); +} + +/** + * hb_blob_set_user_data: (skip) + * @blob: a blob. + * @key: key for data to set. + * @data: data to set. + * @destroy: callback to call when @data is not needed anymore. + * @replace: whether to replace an existing data with the same key. + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_blob_set_user_data (hb_blob_t *blob, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (blob, key, data, destroy, replace); +} + +/** + * hb_blob_get_user_data: (skip) + * @blob: a blob. + * @key: key for data to get. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_blob_get_user_data (hb_blob_t *blob, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (blob, key); +} + + +/** + * hb_blob_make_immutable: + * @blob: a blob. + * + * + * + * Since: 0.9.2 + **/ +void +hb_blob_make_immutable (hb_blob_t *blob) +{ + if (hb_object_is_inert (blob)) + return; + + blob->immutable = true; +} + +/** + * hb_blob_is_immutable: + * @blob: a blob. + * + * + * + * Return value: TODO + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob) +{ + return blob->immutable; +} + + +/** + * hb_blob_get_length: + * @blob: a blob. + * + * + * + * Return value: the length of blob data in bytes. + * + * Since: 0.9.2 + **/ +unsigned int +hb_blob_get_length (hb_blob_t *blob) +{ + return blob->length; +} + +/** + * hb_blob_get_data: + * @blob: a blob. + * @length: (out): + * + * + * + * Returns: (transfer none) (array length=length): + * + * Since: 0.9.2 + **/ +const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length) +{ + if (length) + *length = blob->length; + + return blob->data; +} + +/** + * hb_blob_get_data_writable: + * @blob: a blob. + * @length: (out): output length of the writable data. + * + * Tries to make blob data writable (possibly copying it) and + * return pointer to data. + * + * Fails if blob has been made immutable, or if memory allocation + * fails. + * + * Returns: (transfer none) (array length=length): Writable blob data, + * or %NULL if failed. + * + * Since: 0.9.2 + **/ +char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) +{ + if (!_try_writable (blob)) { + if (length) + *length = 0; + + return NULL; + } + + if (length) + *length = blob->length; + + return const_cast (blob->data); +} + + +static hb_bool_t +_try_make_writable_inplace_unix (hb_blob_t *blob) +{ +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) + uintptr_t pagesize = -1, mask, length; + const char *addr; + +#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); +#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); +#elif defined(HAVE_GETPAGESIZE) + pagesize = (uintptr_t) getpagesize (); +#endif + + if ((uintptr_t) -1L == pagesize) { + DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno)); + return false; + } + DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize); + + mask = ~(pagesize-1); + addr = (const char *) (((uintptr_t) blob->data) & mask); + length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask) - addr; + DEBUG_MSG_FUNC (BLOB, blob, + "calling mprotect on [%p..%p] (%lu bytes)", + addr, addr+length, (unsigned long) length); + if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { + DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno)); + return false; + } + + blob->mode = HB_MEMORY_MODE_WRITABLE; + + DEBUG_MSG_FUNC (BLOB, blob, + "successfully made [%p..%p] (%lu bytes) writable\n", + addr, addr+length, (unsigned long) length); + return true; +#else + return false; +#endif +} + +static bool +_try_writable_inplace (hb_blob_t *blob) +{ + DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n"); + + if (_try_make_writable_inplace_unix (blob)) + return true; + + DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n"); + + /* Failed to make writable inplace, mark that */ + blob->mode = HB_MEMORY_MODE_READONLY; + return false; +} + +static bool +_try_writable (hb_blob_t *blob) +{ + if (blob->immutable) + return false; + + if (blob->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob)) + return true; + + if (blob->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + + DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data); + + char *new_data; + + new_data = (char *) malloc (blob->length); + if (unlikely (!new_data)) + return false; + + DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data); + + memcpy (new_data, blob->data, blob->length); + _hb_blob_destroy_user_data (blob); + blob->mode = HB_MEMORY_MODE_WRITABLE; + blob->data = new_data; + blob->user_data = new_data; + blob->destroy = free; + + return true; +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-blob.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-blob.h new file mode 100644 index 00000000000..6559ca2f407 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-blob.h @@ -0,0 +1,126 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_BLOB_H +#define HB_BLOB_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/* + * Note re various memory-modes: + * + * - In no case shall the HarfBuzz client modify memory + * that is passed to HarfBuzz in a blob. If there is + * any such possibility, MODE_DUPLICATE should be used + * such that HarfBuzz makes a copy immediately, + * + * - Use MODE_READONLY otherse, unless you really really + * really know what you are doing, + * + * - MODE_WRITABLE is appropriate if you really made a + * copy of data solely for the purpose of passing to + * HarfBuzz and doing that just once (no reuse!), + * + * - If the font is mmap()ed, it's ok to use + * READONLY_MAY_MAKE_WRITABLE, however, using that mode + * correctly is very tricky. Use MODE_READONLY instead. + */ +typedef enum { + HB_MEMORY_MODE_DUPLICATE, + HB_MEMORY_MODE_READONLY, + HB_MEMORY_MODE_WRITABLE, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE +} hb_memory_mode_t; + +typedef struct hb_blob_t hb_blob_t; + +hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy); + +/* Always creates with MEMORY_MODE_READONLY. + * Even if the parent blob is writable, we don't + * want the user of the sub-blob to be able to + * modify the parent data as that data may be + * shared among multiple sub-blobs. + */ +hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length); + +hb_blob_t * +hb_blob_get_empty (void); + +hb_blob_t * +hb_blob_reference (hb_blob_t *blob); + +void +hb_blob_destroy (hb_blob_t *blob); + +hb_bool_t +hb_blob_set_user_data (hb_blob_t *blob, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +void * +hb_blob_get_user_data (hb_blob_t *blob, + hb_user_data_key_t *key); + + +void +hb_blob_make_immutable (hb_blob_t *blob); + +hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob); + + +unsigned int +hb_blob_get_length (hb_blob_t *blob); + +const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length); + +char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length); + + +HB_END_DECLS + +#endif /* HB_BLOB_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-deserialize-json.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-deserialize-json.hh new file mode 100644 index 00000000000..5d4387149ac --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-deserialize-json.hh @@ -0,0 +1,643 @@ + +#line 1 "hb-buffer-deserialize-json.rl" +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_JSON_HH +#define HB_BUFFER_DESERIALIZE_JSON_HH + +#include "hb-private.hh" + + +#line 36 "hb-buffer-deserialize-json.hh" +static const unsigned char _deserialize_json_trans_keys[] = { + 0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, + 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, + 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, + 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, + 65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0 +}; + +static const char _deserialize_json_key_spans[] = { + 0, 115, 26, 7, 2, 1, 50, 49, + 10, 117, 117, 117, 1, 50, 49, 10, + 117, 117, 1, 1, 50, 49, 117, 117, + 2, 1, 50, 49, 10, 117, 117, 1, + 50, 49, 10, 117, 117, 1, 50, 49, + 58, 89, 117, 117, 85, 115, 0 +}; + +static const short _deserialize_json_index_offsets[] = { + 0, 0, 116, 143, 151, 154, 156, 207, + 257, 268, 386, 504, 622, 624, 675, 725, + 736, 854, 972, 974, 976, 1027, 1077, 1195, + 1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, + 1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, + 2119, 2178, 2268, 2386, 2504, 2590, 2706 +}; + +static const char _deserialize_json_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 1, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 4, 1, + 5, 1, 6, 7, 1, 1, 8, 1, + 9, 10, 1, 11, 1, 11, 11, 11, + 11, 11, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 11, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 12, 1, + 12, 12, 12, 12, 12, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 12, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 1, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 1, 16, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 1, 18, 18, 18, + 18, 18, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 19, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 20, 1, 21, 21, 21, 21, 21, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 21, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 22, + 1, 18, 18, 18, 18, 18, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 19, 1, 1, 1, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 20, 1, 23, + 1, 23, 23, 23, 23, 23, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 23, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 24, 1, 24, 24, 24, 24, + 24, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 24, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 25, 1, 1, 26, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 1, 28, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 1, 30, 30, 30, 30, 30, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 30, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 31, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 32, 1, 30, + 30, 30, 30, 30, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 30, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 31, 1, 1, 1, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 32, 1, 33, 1, 34, + 1, 34, 34, 34, 34, 34, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 35, 1, 35, 35, 35, 35, + 35, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 35, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 36, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 1, 38, 38, + 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 40, 1, 38, 38, 38, 38, + 38, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 40, 1, 42, 43, 1, 44, 1, 44, + 44, 44, 44, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 44, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 45, 1, 45, 45, 45, 45, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 45, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 46, 1, + 1, 47, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 1, 49, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 1, 51, + 51, 51, 51, 51, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 51, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 52, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 51, 51, 51, + 51, 51, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 52, 1, 1, 1, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 53, 1, 54, 1, 54, 54, 54, + 54, 54, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 54, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 55, 1, + 55, 55, 55, 55, 55, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 55, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 56, 1, 1, 57, + 58, 58, 58, 58, 58, 58, 58, 58, + 58, 1, 59, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 1, 61, 61, 61, + 61, 61, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 61, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 62, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 63, 1, 61, 61, 61, 61, 61, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 61, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 62, 1, + 1, 1, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 63, + 1, 64, 1, 64, 64, 64, 64, 64, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 64, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 65, 1, 65, 65, + 65, 65, 65, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 65, 1, 66, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 67, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 1, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 1, 1, 1, 1, 1, 1, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 1, 70, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 71, 71, + 1, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 1, 1, 1, 1, 1, + 1, 1, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 1, 1, 1, 1, + 71, 1, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 1, 72, 72, 72, + 72, 72, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 72, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 73, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 74, 1, 72, 72, 72, 72, 72, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 72, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 73, 1, + 1, 1, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 74, + 1, 76, 76, 76, 76, 76, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 76, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 77, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 78, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 0 +}; + +static const char _deserialize_json_trans_targs[] = { + 1, 0, 2, 2, 3, 4, 18, 24, + 37, 5, 12, 6, 7, 8, 9, 11, + 9, 11, 10, 2, 44, 10, 44, 13, + 14, 15, 16, 17, 16, 17, 10, 2, + 44, 19, 20, 21, 22, 23, 10, 2, + 44, 23, 25, 31, 26, 27, 28, 29, + 30, 29, 30, 10, 2, 44, 32, 33, + 34, 35, 36, 35, 36, 10, 2, 44, + 38, 39, 40, 42, 43, 41, 10, 41, + 10, 2, 44, 43, 44, 45, 46 +}; + +static const char _deserialize_json_trans_actions[] = { + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 2, + 0, 0, 3, 3, 4, 0, 5, 0, + 0, 2, 2, 2, 0, 0, 6, 6, + 7, 0, 0, 0, 2, 2, 8, 8, + 9, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 10, 10, 11, 0, 0, + 2, 2, 2, 0, 0, 12, 12, 13, + 0, 0, 0, 2, 2, 2, 14, 0, + 15, 15, 16, 0, 0, 0, 0 +}; + +static const int deserialize_json_start = 1; +static const int deserialize_json_first_final = 44; +static const int deserialize_json_error = 0; + +static const int deserialize_json_en_main = 1; + + +#line 97 "hb-buffer-deserialize-json.rl" + + +static hb_bool_t +_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, NULL); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? ',' : '[')) + { + *end_ptr = ++p; + } + + const char *tok = NULL; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 466 "hb-buffer-deserialize-json.hh" + { + cs = deserialize_json_start; + } + +#line 471 "hb-buffer-deserialize-json.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_json_trans_keys + (cs<<1); + _inds = _deserialize_json_indicies + _deserialize_json_index_offsets[cs]; + + _slen = _deserialize_json_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_json_trans_targs[_trans]; + + if ( _deserialize_json_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_json_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-json.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + break; + case 5: +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 2: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} + break; + case 14: +#line 55 "hb-buffer-deserialize-json.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 15: +#line 62 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } + break; + case 8: +#line 63 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 10: +#line 64 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 65 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 3: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 6: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 16: +#line 62 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 63 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 65 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 4: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 7: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 624 "hb-buffer-deserialize-json.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 125 "hb-buffer-deserialize-json.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-deserialize-text.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-deserialize-text.hh new file mode 100644 index 00000000000..c45442c0274 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-deserialize-text.hh @@ -0,0 +1,571 @@ + +#line 1 "hb-buffer-deserialize-text.rl" +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH +#define HB_BUFFER_DESERIALIZE_TEXT_HH + +#include "hb-private.hh" + + +#line 36 "hb-buffer-deserialize-text.hh" +static const unsigned char _deserialize_text_trans_keys[] = { + 0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, + 9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 0 +}; + +static const char _deserialize_text_key_spans[] = { + 0, 114, 13, 10, 13, 10, 10, 13, + 10, 1, 13, 10, 14, 116, 116, 0, + 114, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116 +}; + +static const short _deserialize_text_index_offsets[] = { + 0, 0, 115, 129, 140, 154, 165, 176, + 190, 201, 203, 217, 228, 243, 360, 477, + 478, 593, 710, 827, 944, 1061, 1178, 1295, + 1412, 1529, 1646 +}; + +static const char _deserialize_text_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 1, + 1, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 1, 5, 1, 1, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 1, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 1, 10, 1, 1, + 11, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 1, 13, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 1, 15, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 1, 17, 1, 1, 18, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 1, 20, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 1, 22, 1, 23, 1, 1, 24, + 25, 25, 25, 25, 25, 25, 25, 25, + 25, 1, 26, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 1, 22, 1, 1, + 1, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 1, 28, 28, 28, 28, + 28, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 29, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 30, 1, 1, 31, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 32, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 33, + 1, 34, 34, 34, 34, 34, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 35, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 36, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 1, 1, 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 1, 28, 28, 28, 28, 28, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 28, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 29, 1, 1, 1, + 1, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 1, 1, 1, 30, 1, + 1, 31, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 32, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 33, 1, 38, + 38, 38, 38, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 38, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 39, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 40, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 41, 1, 42, 42, 42, 42, + 42, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 42, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 43, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 44, + 1, 42, 42, 42, 42, 42, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 42, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 43, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 44, 1, 38, 38, + 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 1, 1, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 40, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 41, 1, 45, 45, 45, 45, 45, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 45, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 46, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 48, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 49, 1, + 50, 50, 50, 50, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 50, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 51, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 52, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 50, 50, 50, + 50, 50, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 51, + 1, 1, 1, 1, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 52, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 53, 1, 45, 45, 45, 45, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 45, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 46, 1, 1, 1, + 1, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 1, 1, 1, 1, 1, + 1, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 48, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 49, 1, 28, + 28, 28, 28, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 28, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 29, 1, 55, 55, 1, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 1, 1, 1, 30, 1, 1, 31, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 1, 1, 32, 1, 55, 1, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 1, 33, 1, 0 +}; + +static const char _deserialize_text_trans_targs[] = { + 1, 0, 13, 17, 26, 3, 18, 21, + 18, 21, 5, 19, 20, 19, 20, 22, + 25, 8, 9, 12, 9, 12, 10, 11, + 23, 24, 23, 24, 14, 2, 6, 7, + 15, 16, 14, 15, 16, 17, 14, 4, + 15, 16, 14, 15, 16, 14, 2, 7, + 15, 16, 14, 2, 15, 16, 25, 26 +}; + +static const char _deserialize_text_trans_actions[] = { + 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 2, 2, 2, 0, 0, 2, + 2, 2, 2, 2, 0, 0, 3, 2, + 2, 2, 0, 0, 4, 5, 5, 5, + 4, 4, 0, 0, 0, 0, 6, 7, + 6, 6, 8, 8, 8, 9, 10, 10, + 9, 9, 11, 12, 11, 11, 0, 0 +}; + +static const char _deserialize_text_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, + 0, 4, 6, 8, 8, 6, 9, 11, + 11, 9, 4 +}; + +static const int deserialize_text_start = 1; +static const int deserialize_text_first_final = 13; +static const int deserialize_text_error = 0; + +static const int deserialize_text_en_main = 1; + + +#line 91 "hb-buffer-deserialize-text.rl" + + +static hb_bool_t +_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, NULL); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '[')) + { + *end_ptr = ++p; + } + + const char *eof = pe, *tok = NULL; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 343 "hb-buffer-deserialize-text.hh" + { + cs = deserialize_text_start; + } + +#line 348 "hb-buffer-deserialize-text.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_trans_keys + (cs<<1); + _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs]; + + _slen = _deserialize_text_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_trans_targs[_trans]; + + if ( _deserialize_text_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_trans_actions[_trans] ) { + case 2: +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 5: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 10: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 3: +#line 63 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 7: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 1: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 4: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 6: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 66 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 480 "hb-buffer-deserialize-text.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_eof_actions[cs] ) { + case 4: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 6: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 66 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 557 "hb-buffer-deserialize-text.hh" + } + } + + _out: {} + } + +#line 119 "hb-buffer-deserialize-text.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-private.hh new file mode 100644 index 00000000000..8f0f1d6dd72 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-private.hh @@ -0,0 +1,220 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_PRIVATE_HH +#define HB_BUFFER_PRIVATE_HH + +#include "hb-private.hh" +#include "hb-object-private.hh" +#include "hb-unicode-private.hh" + + +ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20); +ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)); + + +/* + * hb_buffer_t + */ + +struct hb_buffer_t { + hb_object_header_t header; + ASSERT_POD (); + + /* Information about how the text in the buffer should be treated */ + hb_unicode_funcs_t *unicode; /* Unicode functions */ + hb_buffer_flags_t flags; /* BOT / EOT / etc. */ + hb_buffer_cluster_level_t cluster_level; + hb_codepoint_t replacement; /* U+FFFD or something else. */ + + /* Buffer contents */ + hb_buffer_content_type_t content_type; + hb_segment_properties_t props; /* Script, language, direction */ + + bool in_error; /* Allocation failed */ + bool have_output; /* Whether we have an output buffer going on */ + bool have_positions; /* Whether we have positions */ + + unsigned int idx; /* Cursor into ->info and ->pos arrays */ + unsigned int len; /* Length of ->info and ->pos arrays */ + unsigned int out_len; /* Length of ->out array if have_output */ + + unsigned int allocated; /* Length of allocated arrays */ + hb_glyph_info_t *info; + hb_glyph_info_t *out_info; + hb_glyph_position_t *pos; + + inline hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; } + inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; } + + inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; } + inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; } + + inline hb_glyph_info_t &prev (void) { return out_info[out_len - 1]; } + inline hb_glyph_info_t prev (void) const { return info[out_len - 1]; } + + inline bool has_separate_output (void) const { return info != out_info; } + + unsigned int serial; + + /* These reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ + uint8_t allocated_var_bytes[8]; + const char *allocated_var_owner[8]; + + /* Text before / after the main buffer contents. + * Always in Unicode, and ordered outward. + * Index 0 is for "pre-context", 1 for "post-context". */ + static const unsigned int CONTEXT_LENGTH = 5; + hb_codepoint_t context[2][CONTEXT_LENGTH]; + unsigned int context_len[2]; + + + /* Methods */ + + HB_INTERNAL void reset (void); + HB_INTERNAL void clear (void); + + inline unsigned int backtrack_len (void) const + { return have_output? out_len : idx; } + inline unsigned int lookahead_len (void) const + { return len - idx; } + inline unsigned int next_serial (void) { return serial++; } + + HB_INTERNAL void allocate_var (unsigned int byte_i, unsigned int count, const char *owner); + HB_INTERNAL void deallocate_var (unsigned int byte_i, unsigned int count, const char *owner); + HB_INTERNAL void assert_var (unsigned int byte_i, unsigned int count, const char *owner); + HB_INTERNAL void deallocate_var_all (void); + + HB_INTERNAL void add (hb_codepoint_t codepoint, + unsigned int cluster); + HB_INTERNAL void add_info (const hb_glyph_info_t &glyph_info); + + HB_INTERNAL void reverse_range (unsigned int start, unsigned int end); + HB_INTERNAL void reverse (void); + HB_INTERNAL void reverse_clusters (void); + HB_INTERNAL void guess_segment_properties (void); + + HB_INTERNAL void swap_buffers (void); + HB_INTERNAL void remove_output (void); + HB_INTERNAL void clear_output (void); + HB_INTERNAL void clear_positions (void); + + HB_INTERNAL void replace_glyphs (unsigned int num_in, + unsigned int num_out, + const hb_codepoint_t *glyph_data); + + HB_INTERNAL void replace_glyph (hb_codepoint_t glyph_index); + /* Makes a copy of the glyph at idx to output and replace glyph_index */ + HB_INTERNAL void output_glyph (hb_codepoint_t glyph_index); + HB_INTERNAL void output_info (const hb_glyph_info_t &glyph_info); + /* Copies glyph at idx to output but doesn't advance idx */ + HB_INTERNAL void copy_glyph (void); + HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */ + /* Copies glyph at idx to output and advance idx. + * If there's no output, just advance idx. */ + inline void + next_glyph (void) + { + if (have_output) + { + if (unlikely (out_info != info || out_len != idx)) { + if (unlikely (!make_room_for (1, 1))) return; + out_info[out_len] = info[idx]; + } + out_len++; + } + + idx++; + } + + /* Advance idx without copying to output. */ + inline void skip_glyph (void) { idx++; } + + inline void reset_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask = mask; + } + inline void add_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask |= mask; + } + HB_INTERNAL void set_masks (hb_mask_t value, + hb_mask_t mask, + unsigned int cluster_start, + unsigned int cluster_end); + + HB_INTERNAL void merge_clusters (unsigned int start, + unsigned int end) + { + if (end - start < 2) + return; + merge_clusters_impl (start, end); + } + HB_INTERNAL void merge_clusters_impl (unsigned int start, + unsigned int end); + HB_INTERNAL void merge_out_clusters (unsigned int start, + unsigned int end); + /* Merge clusters for deleting current glyph, and skip it. */ + HB_INTERNAL void delete_glyph (void); + + /* Internal methods */ + HB_INTERNAL bool enlarge (unsigned int size); + + inline bool ensure (unsigned int size) + { return likely (!size || size < allocated) ? true : enlarge (size); } + + inline bool ensure_inplace (unsigned int size) + { return likely (!size || size < allocated); } + + HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out); + HB_INTERNAL bool shift_forward (unsigned int count); + + typedef long scratch_buffer_t; + HB_INTERNAL scratch_buffer_t *get_scratch_buffer (unsigned int *size); + + inline void clear_context (unsigned int side) { context_len[side] = 0; } + + HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)); +}; + + +#define HB_BUFFER_XALLOCATE_VAR(b, func, var, owner) \ + b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ + sizeof (b->info[0].var), owner) +#define HB_BUFFER_ALLOCATE_VAR(b, var) \ + HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var (), #var) +#define HB_BUFFER_DEALLOCATE_VAR(b, var) \ + HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var (), #var) +#define HB_BUFFER_ASSERT_VAR(b, var) \ + HB_BUFFER_XALLOCATE_VAR (b, assert_var, var (), #var) + + +#endif /* HB_BUFFER_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-serialize.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-serialize.cc new file mode 100644 index 00000000000..8da89bd3248 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer-serialize.cc @@ -0,0 +1,418 @@ +/* + * Copyright © 2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-buffer-private.hh" + + +static const char *serialize_formats[] = { + "text", + "json", + NULL +}; + +/** + * hb_buffer_serialize_list_formats: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +const char ** +hb_buffer_serialize_list_formats (void) +{ + return serialize_formats; +} + +/** + * hb_buffer_serialize_format_from_string: + * @str: + * @len: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len) +{ + /* Upper-case it. */ + return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020u); +} + +/** + * hb_buffer_serialize_format_to_string: + * @format: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) +{ + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; + case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return NULL; + } +} + +static unsigned int +_hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + NULL : hb_buffer_get_glyph_positions (buffer, NULL); + + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + +#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END + + if (i) + *p++ = ','; + + *p++ = '{'; + + APPEND ("\"g\":"); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + char g[128]; + hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g)); + *p++ = '"'; + for (char *q = g; *q; q++) { + if (*q == '"') + *p++ = '\\'; + *p++ = *q; + } + *p++ = '"'; + } + else + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", + pos[i].x_offset, pos[i].y_offset); + p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", + pos[i].x_advance, pos[i].y_advance); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); + } + + *p++ = '}'; + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + } + + return end - start; +} + +static unsigned int +_hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + NULL : hb_buffer_get_glyph_positions (buffer, NULL); + + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + + if (i) + *p++ = '|'; + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + hb_font_glyph_to_string (font, info[i].codepoint, p, 128); + p += strlen (p); + } + else + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + if (pos[i].x_offset || pos[i].y_offset) + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset)); + + *p++ = '+'; + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + if (pos[i].y_advance) + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + } + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + } + + return end - start; +} + +/* Returns number of items, starting at start, that were serialized. */ +/** + * hb_buffer_serialize_glyphs: + * @buffer: a buffer. + * @start: + * @end: + * @buf: (array length=buf_size): + * @buf_size: + * @buf_consumed: (out): + * @font: + * @format: + * @flags: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +unsigned int +hb_buffer_serialize_glyphs (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + assert (start <= end && end <= buffer->len); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + + assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || + buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (unlikely (start == end)) + return 0; + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_serialize_glyphs_text (buffer, start, end, + buf, buf_size, buf_consumed, + font, flags); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_serialize_glyphs_json (buffer, start, end, + buf, buf_size, buf_consumed, + font, flags); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return 0; + + } +} + + +static hb_bool_t +parse_uint (const char *pp, const char *end, uint32_t *pv) +{ + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); + strncpy (buf, pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + uint32_t v; + + errno = 0; + v = strtol (p, &pend, 10); + if (errno || p == pend || pend - p != end - pp) + return false; + + *pv = v; + return true; +} + +static hb_bool_t +parse_int (const char *pp, const char *end, int32_t *pv) +{ + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); + strncpy (buf, pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + int32_t v; + + errno = 0; + v = strtol (p, &pend, 10); + if (errno || p == pend || pend - p != end - pp) + return false; + + *pv = v; + return true; +} + +#include "hb-buffer-deserialize-json.hh" +#include "hb-buffer-deserialize-text.hh" + +/** + * hb_buffer_deserialize_glyphs: + * @buffer: a buffer. + * @buf: (array length=buf_len): + * @buf_len: + * @end_ptr: (out): + * @font: + * @format: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format) +{ + const char *end; + if (!end_ptr) + end_ptr = &end; + *end_ptr = buf; + + assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || + buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (buf_len == -1) + buf_len = strlen (buf); + + if (!buf_len) + { + *end_ptr = buf; + return false; + } + + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_deserialize_glyphs_text (buffer, + buf, buf_len, end_ptr, + font); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_deserialize_glyphs_json (buffer, + buf, buf_len, end_ptr, + font); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return false; + + } +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer.cc new file mode 100644 index 00000000000..62177f3e9c8 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer.cc @@ -0,0 +1,1701 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-buffer-private.hh" +#include "hb-utf-private.hh" + + +#ifndef HB_DEBUG_BUFFER +#define HB_DEBUG_BUFFER (HB_DEBUG+0) +#endif + + +/** + * Since: 0.9.7 + **/ +hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b) +{ + return a->direction == b->direction && + a->script == b->script && + a->language == b->language && + a->reserved1 == b->reserved1 && + a->reserved2 == b->reserved2; + +} + +/** + * Since: 0.9.7 + **/ +unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p) +{ + return (unsigned int) p->direction ^ + (unsigned int) p->script ^ + (intptr_t) (p->language); +} + + + +/* Here is how the buffer works internally: + * + * There are two info pointers: info and out_info. They always have + * the same allocated size, but different lengths. + * + * As an optimization, both info and out_info may point to the + * same piece of memory, which is owned by info. This remains the + * case as long as out_len doesn't exceed i at any time. + * In that case, swap_buffers() is no-op and the glyph operations operate + * mostly in-place. + * + * As soon as out_info gets longer than info, out_info is moved over + * to an alternate buffer (which we reuse the pos buffer for!), and its + * current contents (out_len entries) are copied to the new place. + * This should all remain transparent to the user. swap_buffers() then + * switches info and out_info. + */ + + + +/* Internal API */ + +bool +hb_buffer_t::enlarge (unsigned int size) +{ + if (unlikely (in_error)) + return false; + + unsigned int new_allocated = allocated; + hb_glyph_position_t *new_pos = NULL; + hb_glyph_info_t *new_info = NULL; + bool separate_out = out_info != info; + + if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0])))) + goto done; + + while (size >= new_allocated) + new_allocated += (new_allocated >> 1) + 32; + + ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0])); + if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0])))) + goto done; + + new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0])); + new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0])); + +done: + if (unlikely (!new_pos || !new_info)) + in_error = true; + + if (likely (new_pos)) + pos = new_pos; + + if (likely (new_info)) + info = new_info; + + out_info = separate_out ? (hb_glyph_info_t *) pos : info; + if (likely (!in_error)) + allocated = new_allocated; + + return likely (!in_error); +} + +bool +hb_buffer_t::make_room_for (unsigned int num_in, + unsigned int num_out) +{ + if (unlikely (!ensure (out_len + num_out))) return false; + + if (out_info == info && + out_len + num_out > idx + num_in) + { + assert (have_output); + + out_info = (hb_glyph_info_t *) pos; + memcpy (out_info, info, out_len * sizeof (out_info[0])); + } + + return true; +} + +bool +hb_buffer_t::shift_forward (unsigned int count) +{ + assert (have_output); + if (unlikely (!ensure (len + count))) return false; + + memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); + len += count; + idx += count; + + return true; +} + +hb_buffer_t::scratch_buffer_t * +hb_buffer_t::get_scratch_buffer (unsigned int *size) +{ + have_output = false; + have_positions = false; + + out_len = 0; + out_info = info; + + assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0); + *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t); + return (scratch_buffer_t *) (void *) pos; +} + + + +/* HarfBuzz-Internal API */ + +void +hb_buffer_t::reset (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + hb_unicode_funcs_destroy (unicode); + unicode = hb_unicode_funcs_get_default (); + flags = HB_BUFFER_FLAG_DEFAULT; + replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + + clear (); +} + +void +hb_buffer_t::clear (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; + props = default_props; + + content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + in_error = false; + have_output = false; + have_positions = false; + + idx = 0; + len = 0; + out_len = 0; + out_info = info; + + serial = 0; + memset (allocated_var_bytes, 0, sizeof allocated_var_bytes); + memset (allocated_var_owner, 0, sizeof allocated_var_owner); + + memset (context, 0, sizeof context); + memset (context_len, 0, sizeof context_len); +} + +void +hb_buffer_t::add (hb_codepoint_t codepoint, + unsigned int cluster) +{ + hb_glyph_info_t *glyph; + + if (unlikely (!ensure (len + 1))) return; + + glyph = &info[len]; + + memset (glyph, 0, sizeof (*glyph)); + glyph->codepoint = codepoint; + glyph->mask = 1; + glyph->cluster = cluster; + + len++; +} + +void +hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) +{ + if (unlikely (!ensure (len + 1))) return; + + info[len] = glyph_info; + + len++; +} + + +void +hb_buffer_t::remove_output (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + have_output = false; + have_positions = false; + + out_len = 0; + out_info = info; +} + +void +hb_buffer_t::clear_output (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + have_output = true; + have_positions = false; + + out_len = 0; + out_info = info; +} + +void +hb_buffer_t::clear_positions (void) +{ + if (unlikely (hb_object_is_inert (this))) + return; + + have_output = false; + have_positions = true; + + out_len = 0; + out_info = info; + + memset (pos, 0, sizeof (pos[0]) * len); +} + +void +hb_buffer_t::swap_buffers (void) +{ + if (unlikely (in_error)) return; + + assert (have_output); + have_output = false; + + if (out_info != info) + { + hb_glyph_info_t *tmp_string; + tmp_string = info; + info = out_info; + out_info = tmp_string; + pos = (hb_glyph_position_t *) out_info; + } + + unsigned int tmp; + tmp = len; + len = out_len; + out_len = tmp; + + idx = 0; +} + + +void +hb_buffer_t::replace_glyphs (unsigned int num_in, + unsigned int num_out, + const uint32_t *glyph_data) +{ + if (unlikely (!make_room_for (num_in, num_out))) return; + + merge_clusters (idx, idx + num_in); + + hb_glyph_info_t orig_info = info[idx]; + hb_glyph_info_t *pinfo = &out_info[out_len]; + for (unsigned int i = 0; i < num_out; i++) + { + *pinfo = orig_info; + pinfo->codepoint = glyph_data[i]; + pinfo++; + } + + idx += num_in; + out_len += num_out; +} + +void +hb_buffer_t::output_glyph (hb_codepoint_t glyph_index) +{ + if (unlikely (!make_room_for (0, 1))) return; + + out_info[out_len] = info[idx]; + out_info[out_len].codepoint = glyph_index; + + out_len++; +} + +void +hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info) +{ + if (unlikely (!make_room_for (0, 1))) return; + + out_info[out_len] = glyph_info; + + out_len++; +} + +void +hb_buffer_t::copy_glyph (void) +{ + if (unlikely (!make_room_for (0, 1))) return; + + out_info[out_len] = info[idx]; + + out_len++; +} + +bool +hb_buffer_t::move_to (unsigned int i) +{ + if (!have_output) + { + assert (i <= len); + idx = i; + return true; + } + + assert (i <= out_len + (len - idx)); + + if (out_len < i) + { + unsigned int count = i - out_len; + if (unlikely (!make_room_for (count, count))) return false; + + memmove (out_info + out_len, info + idx, count * sizeof (out_info[0])); + idx += count; + out_len += count; + } + else if (out_len > i) + { + /* Tricky part: rewinding... */ + unsigned int count = out_len - i; + + if (unlikely (idx < count && !shift_forward (count + 32))) return false; + + assert (idx >= count); + + idx -= count; + out_len -= count; + memmove (info + idx, out_info + out_len, count * sizeof (out_info[0])); + } + + return true; +} + +void +hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index) +{ + if (unlikely (out_info != info || out_len != idx)) { + if (unlikely (!make_room_for (1, 1))) return; + out_info[out_len] = info[idx]; + } + out_info[out_len].codepoint = glyph_index; + + idx++; + out_len++; +} + + +void +hb_buffer_t::set_masks (hb_mask_t value, + hb_mask_t mask, + unsigned int cluster_start, + unsigned int cluster_end) +{ + hb_mask_t not_mask = ~mask; + value &= mask; + + if (!mask) + return; + + if (cluster_start == 0 && cluster_end == (unsigned int)-1) { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + info[i].mask = (info[i].mask & not_mask) | value; + return; + } + + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) + info[i].mask = (info[i].mask & not_mask) | value; +} + +void +hb_buffer_t::reverse_range (unsigned int start, + unsigned int end) +{ + unsigned int i, j; + + if (end - start < 2) + return; + + for (i = start, j = end - 1; i < j; i++, j--) { + hb_glyph_info_t t; + + t = info[i]; + info[i] = info[j]; + info[j] = t; + } + + if (have_positions) { + for (i = start, j = end - 1; i < j; i++, j--) { + hb_glyph_position_t t; + + t = pos[i]; + pos[i] = pos[j]; + pos[j] = t; + } + } +} + +void +hb_buffer_t::reverse (void) +{ + if (unlikely (!len)) + return; + + reverse_range (0, len); +} + +void +hb_buffer_t::reverse_clusters (void) +{ + unsigned int i, start, count, last_cluster; + + if (unlikely (!len)) + return; + + reverse (); + + count = len; + start = 0; + last_cluster = info[0].cluster; + for (i = 1; i < count; i++) { + if (last_cluster != info[i].cluster) { + reverse_range (start, i); + start = i; + last_cluster = info[i].cluster; + } + } + reverse_range (start, i); +} + +void +hb_buffer_t::merge_clusters_impl (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + return; + + unsigned int cluster = info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = MIN (cluster, info[i].cluster); + + /* Extend end */ + while (end < len && info[end - 1].cluster == info[end].cluster) + end++; + + /* Extend start */ + while (idx < start && info[start - 1].cluster == info[start].cluster) + start--; + + /* If we hit the start of buffer, continue in out-buffer. */ + if (idx == start) + for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) + out_info[i - 1].cluster = cluster; + + for (unsigned int i = start; i < end; i++) + info[i].cluster = cluster; +} +void +hb_buffer_t::merge_out_clusters (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + return; + + if (unlikely (end - start < 2)) + return; + + unsigned int cluster = out_info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = MIN (cluster, out_info[i].cluster); + + /* Extend start */ + while (start && out_info[start - 1].cluster == out_info[start].cluster) + start--; + + /* Extend end */ + while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster) + end++; + + /* If we hit the end of out-buffer, continue in buffer. */ + if (end == out_len) + for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++) + info[i].cluster = cluster; + + for (unsigned int i = start; i < end; i++) + out_info[i].cluster = cluster; +} +void +hb_buffer_t::delete_glyph () +{ + unsigned int cluster = info[idx].cluster; + if (idx + 1 < len && cluster == info[idx + 1].cluster) + { + /* Cluster survives; do nothing. */ + goto done; + } + + if (out_len) + { + /* Merge cluster backward. */ + if (cluster < out_info[out_len - 1].cluster) + { + unsigned int old_cluster = out_info[out_len - 1].cluster; + for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--) + out_info[i - 1].cluster = cluster; + } + goto done; + } + + if (idx + 1 < len) + { + /* Merge cluster forward. */ + merge_clusters (idx, idx + 2); + goto done; + } + +done: + skip_glyph (); +} + +void +hb_buffer_t::guess_segment_properties (void) +{ + assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || + (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + + /* If script is set to INVALID, guess from buffer contents */ + if (props.script == HB_SCRIPT_INVALID) { + for (unsigned int i = 0; i < len; i++) { + hb_script_t script = unicode->script (info[i].codepoint); + if (likely (script != HB_SCRIPT_COMMON && + script != HB_SCRIPT_INHERITED && + script != HB_SCRIPT_UNKNOWN)) { + props.script = script; + break; + } + } + } + + /* If direction is set to INVALID, guess from script */ + if (props.direction == HB_DIRECTION_INVALID) { + props.direction = hb_script_get_horizontal_direction (props.script); + } + + /* If language is not set, use default language from locale */ + if (props.language == HB_LANGUAGE_INVALID) { + /* TODO get_default_for_script? using $LANGUAGE */ + props.language = hb_language_get_default (); + } +} + + +static inline void +dump_var_allocation (const hb_buffer_t *buffer) +{ + char buf[80]; + for (unsigned int i = 0; i < 8; i++) + buf[i] = '0' + buffer->allocated_var_bytes[7 - i]; + buf[8] = '\0'; + DEBUG_MSG (BUFFER, buffer, + "Current var allocation: %s", + buf); +} + +void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const char *owner) +{ + assert (byte_i < 8 && byte_i + count <= 8); + + if (DEBUG_ENABLED (BUFFER)) + dump_var_allocation (this); + DEBUG_MSG (BUFFER, this, + "Allocating var bytes %d..%d for %s", + byte_i, byte_i + count - 1, owner); + + for (unsigned int i = byte_i; i < byte_i + count; i++) { + assert (!allocated_var_bytes[i]); + allocated_var_bytes[i]++; + allocated_var_owner[i] = owner; + } +} + +void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const char *owner) +{ + if (DEBUG_ENABLED (BUFFER)) + dump_var_allocation (this); + + DEBUG_MSG (BUFFER, this, + "Deallocating var bytes %d..%d for %s", + byte_i, byte_i + count - 1, owner); + + assert (byte_i < 8 && byte_i + count <= 8); + for (unsigned int i = byte_i; i < byte_i + count; i++) { + assert (allocated_var_bytes[i]); + assert (0 == strcmp (allocated_var_owner[i], owner)); + allocated_var_bytes[i]--; + } +} + +void hb_buffer_t::assert_var (unsigned int byte_i, unsigned int count, const char *owner) +{ + if (DEBUG_ENABLED (BUFFER)) + dump_var_allocation (this); + + DEBUG_MSG (BUFFER, this, + "Asserting var bytes %d..%d for %s", + byte_i, byte_i + count - 1, owner); + + assert (byte_i < 8 && byte_i + count <= 8); + for (unsigned int i = byte_i; i < byte_i + count; i++) { + assert (allocated_var_bytes[i]); + assert (0 == strcmp (allocated_var_owner[i], owner)); + } +} + +void hb_buffer_t::deallocate_var_all (void) +{ + memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes)); + memset (allocated_var_owner, 0, sizeof (allocated_var_owner)); +} + +/* Public API */ + +/** + * hb_buffer_create: (Xconstructor) + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_create (void) +{ + hb_buffer_t *buffer; + + if (!(buffer = hb_object_create ())) + return hb_buffer_get_empty (); + + buffer->reset (); + + return buffer; +} + +/** + * hb_buffer_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_get_empty (void) +{ + static const hb_buffer_t _hb_buffer_nil = { + HB_OBJECT_HEADER_STATIC, + + const_cast (&_hb_unicode_funcs_nil), + HB_BUFFER_FLAG_DEFAULT, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, + + HB_BUFFER_CONTENT_TYPE_INVALID, + HB_SEGMENT_PROPERTIES_DEFAULT, + true, /* in_error */ + true, /* have_output */ + true /* have_positions */ + + /* Zero is good enough for everything else. */ + }; + + return const_cast (&_hb_buffer_nil); +} + +/** + * hb_buffer_reference: (skip) + * @buffer: a buffer. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer) +{ + return hb_object_reference (buffer); +} + +/** + * hb_buffer_destroy: (skip) + * @buffer: a buffer. + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_destroy (hb_buffer_t *buffer) +{ + if (!hb_object_destroy (buffer)) return; + + hb_unicode_funcs_destroy (buffer->unicode); + + free (buffer->info); + free (buffer->pos); + + free (buffer); +} + +/** + * hb_buffer_set_user_data: (skip) + * @buffer: a buffer. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_set_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (buffer, key, data, destroy, replace); +} + +/** + * hb_buffer_get_user_data: (skip) + * @buffer: a buffer. + * @key: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +void * +hb_buffer_get_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (buffer, key); +} + + +/** + * hb_buffer_set_content_type: + * @buffer: a buffer. + * @content_type: + * + * + * + * Since: 0.9.5 + **/ +void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type) +{ + buffer->content_type = content_type; +} + +/** + * hb_buffer_get_content_type: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 0.9.5 + **/ +hb_buffer_content_type_t +hb_buffer_get_content_type (hb_buffer_t *buffer) +{ + return buffer->content_type; +} + + +/** + * hb_buffer_set_unicode_funcs: + * @buffer: a buffer. + * @unicode_funcs: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + if (!unicode_funcs) + unicode_funcs = hb_unicode_funcs_get_default (); + + + hb_unicode_funcs_reference (unicode_funcs); + hb_unicode_funcs_destroy (buffer->unicode); + buffer->unicode = unicode_funcs; +} + +/** + * hb_buffer_get_unicode_funcs: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) +{ + return buffer->unicode; +} + +/** + * hb_buffer_set_direction: + * @buffer: a buffer. + * @direction: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction) + +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->props.direction = direction; +} + +/** + * hb_buffer_get_direction: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_buffer_get_direction (hb_buffer_t *buffer) +{ + return buffer->props.direction; +} + +/** + * hb_buffer_set_script: + * @buffer: a buffer. + * @script: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->props.script = script; +} + +/** + * hb_buffer_get_script: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_script_t +hb_buffer_get_script (hb_buffer_t *buffer) +{ + return buffer->props.script; +} + +/** + * hb_buffer_set_language: + * @buffer: a buffer. + * @language: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->props.language = language; +} + +/** + * hb_buffer_get_language: + * @buffer: a buffer. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_language_t +hb_buffer_get_language (hb_buffer_t *buffer) +{ + return buffer->props.language; +} + +/** + * hb_buffer_set_segment_properties: + * @buffer: a buffer. + * @props: + * + * + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->props = *props; +} + +/** + * hb_buffer_get_segment_properties: + * @buffer: a buffer. + * @props: (out): + * + * + * + * Since: 0.9.7 + **/ +void +hb_buffer_get_segment_properties (hb_buffer_t *buffer, + hb_segment_properties_t *props) +{ + *props = buffer->props; +} + + +/** + * hb_buffer_set_flags: + * @buffer: a buffer. + * @flags: + * + * + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->flags = flags; +} + +/** + * hb_buffer_get_flags: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +hb_buffer_flags_t +hb_buffer_get_flags (hb_buffer_t *buffer) +{ + return buffer->flags; +} + +/** + * hb_buffer_set_cluster_level: + * @buffer: a buffer. + * @cluster_level: + * + * + * + * Since: 0.9.42 + **/ +void +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->cluster_level = cluster_level; +} + +/** + * hb_buffer_get_cluster_level: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 0.9.42 + **/ +hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (hb_buffer_t *buffer) +{ + return buffer->cluster_level; +} + + +/** + * hb_buffer_set_replacement_codepoint: + * @buffer: a buffer. + * @replacement: + * + * + * + * Since: 0.9.31 + **/ +void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement) +{ + if (unlikely (hb_object_is_inert (buffer))) + return; + + buffer->replacement = replacement; +} + +/** + * hb_buffer_get_replacement_codepoint: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 0.9.31 + **/ +hb_codepoint_t +hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer) +{ + return buffer->replacement; +} + + +/** + * hb_buffer_reset: + * @buffer: a buffer. + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_reset (hb_buffer_t *buffer) +{ + buffer->reset (); +} + +/** + * hb_buffer_clear_contents: + * @buffer: a buffer. + * + * + * + * Since: 0.9.11 + **/ +void +hb_buffer_clear_contents (hb_buffer_t *buffer) +{ + buffer->clear (); +} + +/** + * hb_buffer_pre_allocate: + * @buffer: a buffer. + * @size: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) +{ + return buffer->ensure (size); +} + +/** + * hb_buffer_allocation_successful: + * @buffer: a buffer. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer) +{ + return !buffer->in_error; +} + +/** + * hb_buffer_add: + * @buffer: a buffer. + * @codepoint: + * @cluster: + * + * + * + * Since: 0.9.7 + **/ +void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster) +{ + buffer->add (codepoint, cluster); + buffer->clear_context (1); +} + +/** + * hb_buffer_set_length: + * @buffer: a buffer. + * @length: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length) +{ + if (unlikely (hb_object_is_inert (buffer))) + return length == 0; + + if (!buffer->ensure (length)) + return false; + + /* Wipe the new space */ + if (length > buffer->len) { + memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + if (buffer->have_positions) + memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + } + + buffer->len = length; + + if (!length) + { + buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + buffer->clear_context (0); + } + buffer->clear_context (1); + + return true; +} + +/** + * hb_buffer_get_length: + * @buffer: a buffer. + * + * Returns the number of items in the buffer. + * + * Return value: buffer length. + * + * Since: 0.9.2 + **/ +unsigned int +hb_buffer_get_length (hb_buffer_t *buffer) +{ + return buffer->len; +} + +/** + * hb_buffer_get_glyph_infos: + * @buffer: a buffer. + * @length: (out): output array length. + * + * Returns buffer glyph information array. Returned pointer + * is valid as long as buffer contents are not modified. + * + * Return value: (transfer none) (array length=length): buffer glyph information array. + * + * Since: 0.9.2 + **/ +hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length) +{ + if (length) + *length = buffer->len; + + return (hb_glyph_info_t *) buffer->info; +} + +/** + * hb_buffer_get_glyph_positions: + * @buffer: a buffer. + * @length: (out): output length. + * + * Returns buffer glyph position array. Returned pointer + * is valid as long as buffer contents are not modified. + * + * Return value: (transfer none) (array length=length): buffer glyph position array. + * + * Since: 0.9.2 + **/ +hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length) +{ + if (!buffer->have_positions) + buffer->clear_positions (); + + if (length) + *length = buffer->len; + + return (hb_glyph_position_t *) buffer->pos; +} + +/** + * hb_buffer_reverse: + * @buffer: a buffer. + * + * Reverses buffer contents. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse (hb_buffer_t *buffer) +{ + buffer->reverse (); +} + +/** + * hb_buffer_reverse_range: + * @buffer: a buffer. + * @start: start index. + * @end: end index. + * + * Reverses buffer contents between start to end. + * + * Since: 0.9.41 + **/ +void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + buffer->reverse_range (start, end); +} + +/** + * hb_buffer_reverse_clusters: + * @buffer: a buffer. + * + * Reverses buffer clusters. That is, the buffer contents are + * reversed, then each cluster (consecutive items having the + * same cluster number) are reversed again. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse_clusters (hb_buffer_t *buffer) +{ + buffer->reverse_clusters (); +} + +/** + * hb_buffer_guess_segment_properties: + * @buffer: a buffer. + * + * Sets unset buffer segment properties based on buffer Unicode + * contents. If buffer is not empty, it must have content type + * %HB_BUFFER_CONTENT_TYPE_UNICODE. + * + * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it + * will be set to the Unicode script of the first character in + * the buffer that has a script other than %HB_SCRIPT_COMMON, + * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN. + * + * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID), + * it will be set to the natural horizontal direction of the + * buffer script as returned by hb_script_get_horizontal_direction(). + * + * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID), + * it will be set to the process's default language as returned by + * hb_language_get_default(). This may change in the future by + * taking buffer script into consideration when choosing a language. + * + * Since: 0.9.7 + **/ +void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer) +{ + buffer->guess_segment_properties (); +} + +template +static inline void +hb_buffer_add_utf (hb_buffer_t *buffer, + const typename utf_t::codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + typedef typename utf_t::codepoint_t T; + const hb_codepoint_t replacement = buffer->replacement; + + assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || + (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + + if (unlikely (hb_object_is_inert (buffer))) + return; + + if (text_length == -1) + text_length = utf_t::strlen (text); + + if (item_length == -1) + item_length = text_length - item_offset; + + buffer->ensure (buffer->len + item_length * sizeof (T) / 4); + + /* If buffer is empty and pre-context provided, install it. + * This check is written this way, to make sure people can + * provide pre-context in one add_utf() call, then provide + * text in a follow-up call. See: + * + * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 + */ + if (!buffer->len && item_offset > 0) + { + /* Add pre-context */ + buffer->clear_context (0); + const T *prev = text + item_offset; + const T *start = text; + while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + prev = utf_t::prev (prev, start, &u, replacement); + buffer->context[0][buffer->context_len[0]++] = u; + } + } + + const T *next = text + item_offset; + const T *end = next + item_length; + while (next < end) + { + hb_codepoint_t u; + const T *old_next = next; + next = utf_t::next (next, end, &u, replacement); + buffer->add (u, old_next - (const T *) text); + } + + /* Add post-context */ + buffer->clear_context (1); + end = text + text_length; + while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + next = utf_t::next (next, end, &u, replacement); + buffer->context[1][buffer->context_len[1]++] = u; + } + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; +} + +/** + * hb_buffer_add_utf8: + * @buffer: a buffer. + * @text: (array length=text_length) (element-type uint8_t): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, (const uint8_t *) text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_utf16: + * @buffer: a buffer. + * @text: (array length=text_length): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_utf32: + * @buffer: a buffer. + * @text: (array length=text_length): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf > (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_latin1: + * @buffer: a buffer. + * @text: (array length=text_length) (element-type uint8_t): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 0.9.39 + **/ +void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_codepoints: + * @buffer: a buffer. + * @text: (array length=text_length): + * @text_length: + * @item_offset: + * @item_length: + * + * + * + * Since: 0.9.31 + **/ +void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf > (buffer, text, text_length, item_offset, item_length); +} + + +static int +compare_info_codepoint (const hb_glyph_info_t *pa, + const hb_glyph_info_t *pb) +{ + return (int) pb->codepoint - (int) pa->codepoint; +} + +static inline void +normalize_glyphs_cluster (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool backward) +{ + hb_glyph_position_t *pos = buffer->pos; + + /* Total cluster advance */ + hb_position_t total_x_advance = 0, total_y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + total_x_advance += pos[i].x_advance; + total_y_advance += pos[i].y_advance; + } + + hb_position_t x_advance = 0, y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + pos[i].x_offset += x_advance; + pos[i].y_offset += y_advance; + + x_advance += pos[i].x_advance; + y_advance += pos[i].y_advance; + + pos[i].x_advance = 0; + pos[i].y_advance = 0; + } + + if (backward) + { + /* Transfer all cluster advance to the last glyph. */ + pos[end - 1].x_advance = total_x_advance; + pos[end - 1].y_advance = total_y_advance; + + hb_stable_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); + } else { + /* Transfer all cluster advance to the first glyph. */ + pos[start].x_advance += total_x_advance; + pos[start].y_advance += total_y_advance; + for (unsigned int i = start + 1; i < end; i++) { + pos[i].x_offset -= total_x_advance; + pos[i].y_offset -= total_y_advance; + } + hb_stable_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); + } +} + +/** + * hb_buffer_normalize_glyphs: + * @buffer: a buffer. + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer) +{ + assert (buffer->have_positions); + assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + unsigned int count = buffer->len; + if (unlikely (!count)) return; + hb_glyph_info_t *info = buffer->info; + + unsigned int start = 0; + unsigned int end; + for (end = start + 1; end < count; end++) + if (info[start].cluster != info[end].cluster) { + normalize_glyphs_cluster (buffer, start, end, backward); + start = end; + } + normalize_glyphs_cluster (buffer, start, end, backward); +} + +void +hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)) +{ + assert (!have_positions); + for (unsigned int i = start + 1; i < end; i++) + { + unsigned int j = i; + while (j > start && compar (&info[j - 1], &info[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + merge_clusters (j, i + 1); + { + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t)); + info[j] = t; + } + } +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer.h new file mode 100644 index 00000000000..5e56da49355 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-buffer.h @@ -0,0 +1,378 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_BUFFER_H +#define HB_BUFFER_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + + +typedef struct hb_glyph_info_t { + hb_codepoint_t codepoint; + hb_mask_t mask; + uint32_t cluster; + + /*< private >*/ + hb_var_int_t var1; + hb_var_int_t var2; +} hb_glyph_info_t; + +typedef struct hb_glyph_position_t { + hb_position_t x_advance; + hb_position_t y_advance; + hb_position_t x_offset; + hb_position_t y_offset; + + /*< private >*/ + hb_var_int_t var; +} hb_glyph_position_t; + + +typedef struct hb_segment_properties_t { + hb_direction_t direction; + hb_script_t script; + hb_language_t language; + /*< private >*/ + void *reserved1; + void *reserved2; +} hb_segment_properties_t; + +#define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \ + HB_SCRIPT_INVALID, \ + HB_LANGUAGE_INVALID, \ + NULL, \ + NULL} + +hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b); + +unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p); + + + +/* + * hb_buffer_t + */ + +typedef struct hb_buffer_t hb_buffer_t; + +hb_buffer_t * +hb_buffer_create (void); + +hb_buffer_t * +hb_buffer_get_empty (void); + +hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer); + +void +hb_buffer_destroy (hb_buffer_t *buffer); + +hb_bool_t +hb_buffer_set_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +void * +hb_buffer_get_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key); + + +typedef enum { + HB_BUFFER_CONTENT_TYPE_INVALID = 0, + HB_BUFFER_CONTENT_TYPE_UNICODE, + HB_BUFFER_CONTENT_TYPE_GLYPHS +} hb_buffer_content_type_t; + +void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type); + +hb_buffer_content_type_t +hb_buffer_get_content_type (hb_buffer_t *buffer); + + +void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs); + +hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (hb_buffer_t *buffer); + +void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction); + +hb_direction_t +hb_buffer_get_direction (hb_buffer_t *buffer); + +void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script); + +hb_script_t +hb_buffer_get_script (hb_buffer_t *buffer); + +void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language); + + +hb_language_t +hb_buffer_get_language (hb_buffer_t *buffer); + +void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props); + +void +hb_buffer_get_segment_properties (hb_buffer_t *buffer, + hb_segment_properties_t *props); + +void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer); + + +/* + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ + HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ + HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u +} hb_buffer_flags_t; + +void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags); + +hb_buffer_flags_t +hb_buffer_get_flags (hb_buffer_t *buffer); + +/* + * Since: 0.9.42 + */ +typedef enum { + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES = 0, + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = 1, + HB_BUFFER_CLUSTER_LEVEL_CHARACTERS = 2, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES +} hb_buffer_cluster_level_t; + +void +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level); + +hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (hb_buffer_t *buffer); + +#define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu + +/* Sets codepoint used to replace invalid UTF-8/16/32 entries. + * Default is 0xFFFDu. */ +void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement); + +hb_codepoint_t +hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer); + + +/* Resets the buffer. Afterwards it's as if it was just created, + * except that it has a larger buffer allocated perhaps... */ +void +hb_buffer_reset (hb_buffer_t *buffer); + +/* Like reset, but does NOT clear unicode_funcs and replacement_codepoint. */ +void +hb_buffer_clear_contents (hb_buffer_t *buffer); + +/* Returns false if allocation failed */ +hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, + unsigned int size); + + +/* Returns false if allocation has failed before */ +hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer); + +void +hb_buffer_reverse (hb_buffer_t *buffer); + +void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end); + +void +hb_buffer_reverse_clusters (hb_buffer_t *buffer); + + +/* Filling the buffer in */ + +void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster); + +void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length); + +void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +/* Allows only access to first 256 Unicode codepoints. */ +void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +/* Like add_utf32 but does NOT check for invalid Unicode codepoints. */ +void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length); + + +/* Clears any new items added at the end */ +hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length); + +/* Return value valid as long as buffer not modified */ +unsigned int +hb_buffer_get_length (hb_buffer_t *buffer); + +/* Getting glyphs out of the buffer */ + +/* Return value valid as long as buffer not modified */ +hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length); + +/* Return value valid as long as buffer not modified */ +hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length); + + +/* Reorders a glyph buffer to have canonical in-cluster glyph order / position. + * The resulting clusters should behave identical to pre-reordering clusters. + * NOTE: This has nothing to do with Unicode normalization. */ +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer); + + +/* + * Serialize + */ + +/* + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_SERIALIZE_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS = 0x00000001u, + HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS = 0x00000002u, + HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 0x00000004u, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS = 0x00000008u +} hb_buffer_serialize_flags_t; + +typedef enum { + HB_BUFFER_SERIALIZE_FORMAT_TEXT = HB_TAG('T','E','X','T'), + HB_BUFFER_SERIALIZE_FORMAT_JSON = HB_TAG('J','S','O','N'), + HB_BUFFER_SERIALIZE_FORMAT_INVALID = HB_TAG_NONE +} hb_buffer_serialize_format_t; + +/* len=-1 means str is NUL-terminated. */ +hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len); + +const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format); + +const char ** +hb_buffer_serialize_list_formats (void); + +/* Returns number of items, starting at start, that were serialized. */ +unsigned int +hb_buffer_serialize_glyphs (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +hb_bool_t +hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format); + + +HB_END_DECLS + +#endif /* HB_BUFFER_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-cache-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-cache-private.hh new file mode 100644 index 00000000000..19b70b7e395 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-cache-private.hh @@ -0,0 +1,74 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_CACHE_PRIVATE_HH +#define HB_CACHE_PRIVATE_HH + +#include "hb-private.hh" + + +/* Implements a lock-free cache for int->int functions. */ + +template +struct hb_cache_t +{ + ASSERT_STATIC (key_bits >= cache_bits); + ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int)); + + inline void clear (void) + { + memset (values, 255, sizeof (values)); + } + + inline bool get (unsigned int key, unsigned int *value) + { + unsigned int k = key & ((1<> value_bits) != (key >> cache_bits)) + return false; + *value = v & ((1<> key_bits) || (value >> value_bits))) + return false; /* Overflows */ + unsigned int k = key & ((1<>cache_bits)< hb_cmap_cache_t; +typedef hb_cache_t<16, 24, 8> hb_advance_cache_t; + + +#endif /* HB_CACHE_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.cc new file mode 100644 index 00000000000..bf5dba8da0c --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.cc @@ -0,0 +1,593 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-mutex-private.hh" +#include "hb-object-private.hh" + +#include + + +/* hb_options_t */ + +hb_options_union_t _hb_options; + +void +_hb_options_init (void) +{ + hb_options_union_t u; + u.i = 0; + u.opts.initialized = 1; + + char *c = getenv ("HB_OPTIONS"); + u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); + + /* This is idempotent and threadsafe. */ + _hb_options = u; +} + + +/* hb_tag_t */ + +/** + * hb_tag_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_tag_from_string (const char *str, int len) +{ + char tag[4]; + unsigned int i; + + if (!str || !len || !*str) + return HB_TAG_NONE; + + if (len < 0 || len > 4) + len = 4; + for (i = 0; i < (unsigned) len && str[i]; i++) + tag[i] = str[i]; + for (; i < 4; i++) + tag[i] = ' '; + + return HB_TAG_CHAR4 (tag); +} + +/** + * hb_tag_to_string: + * @tag: + * @buf: (array fixed-size=4): + * + * + * + * Since: 0.9.5 + **/ +void +hb_tag_to_string (hb_tag_t tag, char *buf) +{ + buf[0] = (char) (uint8_t) (tag >> 24); + buf[1] = (char) (uint8_t) (tag >> 16); + buf[2] = (char) (uint8_t) (tag >> 8); + buf[3] = (char) (uint8_t) (tag >> 0); +} + + +/* hb_direction_t */ + +const char direction_strings[][4] = { + "ltr", + "rtl", + "ttb", + "btt" +}; + +/** + * hb_direction_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_direction_from_string (const char *str, int len) +{ + if (unlikely (!str || !len || !*str)) + return HB_DIRECTION_INVALID; + + /* Lets match loosely: just match the first letter, such that + * all of "ltr", "left-to-right", etc work! + */ + char c = TOLOWER (str[0]); + for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) + if (c == direction_strings[i][0]) + return (hb_direction_t) (HB_DIRECTION_LTR + i); + + return HB_DIRECTION_INVALID; +} + +/** + * hb_direction_to_string: + * @direction: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +const char * +hb_direction_to_string (hb_direction_t direction) +{ + if (likely ((unsigned int) (direction - HB_DIRECTION_LTR) + < ARRAY_LENGTH (direction_strings))) + return direction_strings[direction - HB_DIRECTION_LTR]; + + return "invalid"; +} + + +/* hb_language_t */ + +struct hb_language_impl_t { + const char s[1]; +}; + +static const char canon_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, + '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 +}; + +static bool +lang_equal (hb_language_t v1, + const void *v2) +{ + const unsigned char *p1 = (const unsigned char *) v1; + const unsigned char *p2 = (const unsigned char *) v2; + + while (*p1 && *p1 == canon_map[*p2]) + p1++, p2++; + + return *p1 == canon_map[*p2]; +} + +#if 0 +static unsigned int +lang_hash (const void *key) +{ + const unsigned char *p = key; + unsigned int h = 0; + while (canon_map[*p]) + { + h = (h << 5) - h + canon_map[*p]; + p++; + } + + return h; +} +#endif + + +struct hb_language_item_t { + + struct hb_language_item_t *next; + hb_language_t lang; + + inline bool operator == (const char *s) const { + return lang_equal (lang, s); + } + + inline hb_language_item_t & operator = (const char *s) { + lang = (hb_language_t) strdup (s); + for (unsigned char *p = (unsigned char *) lang; *p; p++) + *p = canon_map[*p]; + + return *this; + } + + void finish (void) { free ((void *) lang); } +}; + + +/* Thread-safe lock-free language list */ + +static hb_language_item_t *langs; + +#ifdef HB_USE_ATEXIT +static +void free_langs (void) +{ + while (langs) { + hb_language_item_t *next = langs->next; + langs->finish (); + free (langs); + langs = next; + } +} +#endif + +static hb_language_item_t * +lang_find_or_insert (const char *key) +{ +retry: + hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs); + + for (hb_language_item_t *lang = first_lang; lang; lang = lang->next) + if (*lang == key) + return lang; + + /* Not found; allocate one. */ + hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); + if (unlikely (!lang)) + return NULL; + lang->next = first_lang; + *lang = key; + + if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { + lang->finish (); + free (lang); + goto retry; + } + +#ifdef HB_USE_ATEXIT + if (!first_lang) + atexit (free_langs); /* First person registers atexit() callback. */ +#endif + + return lang; +} + + +/** + * hb_language_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_from_string (const char *str, int len) +{ + if (!str || !len || !*str) + return HB_LANGUAGE_INVALID; + + hb_language_item_t *item = NULL; + if (len >= 0) + { + /* NUL-terminate it. */ + char strbuf[64]; + len = MIN (len, (int) sizeof (strbuf) - 1); + memcpy (strbuf, str, len); + strbuf[len] = '\0'; + item = lang_find_or_insert (strbuf); + } + else + item = lang_find_or_insert (str); + + return likely (item) ? item->lang : HB_LANGUAGE_INVALID; +} + +/** + * hb_language_to_string: + * @language: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +const char * +hb_language_to_string (hb_language_t language) +{ + /* This is actually NULL-safe! */ + return language->s; +} + +/** + * hb_language_get_default: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_get_default (void) +{ + static hb_language_t default_language = HB_LANGUAGE_INVALID; + + hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); + if (unlikely (language == HB_LANGUAGE_INVALID)) { + language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1); + (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); + } + + return default_language; +} + + +/* hb_script_t */ + +/** + * hb_script_from_iso15924_tag: + * @tag: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag) +{ + if (unlikely (tag == HB_TAG_NONE)) + return HB_SCRIPT_INVALID; + + /* Be lenient, adjust case (one capital letter followed by three small letters) */ + tag = (tag & 0xDFDFDFDFu) | 0x00202020u; + + switch (tag) { + + /* These graduated from the 'Q' private-area codes, but + * the old code is still aliased by Unicode, and the Qaai + * one in use by ICU. */ + case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; + case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; + + /* Script variants from http://unicode.org/iso15924/ */ + case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; + case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; + case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; + case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; + } + + /* If it looks right, just use the tag as a script */ + if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u) + return (hb_script_t) tag; + + /* Otherwise, return unknown */ + return HB_SCRIPT_UNKNOWN; +} + +/** + * hb_script_from_string: + * @s: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_string (const char *s, int len) +{ + return hb_script_from_iso15924_tag (hb_tag_from_string (s, len)); +} + +/** + * hb_script_to_iso15924_tag: + * @script: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script) +{ + return (hb_tag_t) script; +} + +/** + * hb_script_get_horizontal_direction: + * @script: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_script_get_horizontal_direction (hb_script_t script) +{ + /* http://goo.gl/x9ilM */ + switch ((hb_tag_t) script) + { + /* Unicode-1.1 additions */ + case HB_SCRIPT_ARABIC: + case HB_SCRIPT_HEBREW: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_SYRIAC: + case HB_SCRIPT_THAANA: + + /* Unicode-4.0 additions */ + case HB_SCRIPT_CYPRIOT: + + /* Unicode-4.1 additions */ + case HB_SCRIPT_KHAROSHTHI: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_PHOENICIAN: + case HB_SCRIPT_NKO: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_LYDIAN: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_AVESTAN: + case HB_SCRIPT_IMPERIAL_ARAMAIC: + case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: + case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: + case HB_SCRIPT_OLD_SOUTH_ARABIAN: + case HB_SCRIPT_OLD_TURKIC: + case HB_SCRIPT_SAMARITAN: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_MANDAIC: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_MEROITIC_CURSIVE: + case HB_SCRIPT_MEROITIC_HIEROGLYPHS: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MENDE_KIKAKUI: + case HB_SCRIPT_NABATAEAN: + case HB_SCRIPT_OLD_NORTH_ARABIAN: + case HB_SCRIPT_PALMYRENE: + case HB_SCRIPT_PSALTER_PAHLAVI: + + /* Unicode-8.0 additions */ + case HB_SCRIPT_OLD_HUNGARIAN: + + return HB_DIRECTION_RTL; + } + + return HB_DIRECTION_LTR; +} + + +/* hb_user_data_array_t */ + +bool +hb_user_data_array_t::set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (!key) + return false; + + if (replace) { + if (!data && !destroy) { + items.remove (key, lock); + return true; + } + } + hb_user_data_item_t item = {key, data, destroy}; + bool ret = !!items.replace_or_insert (item, lock, replace); + + return ret; +} + +void * +hb_user_data_array_t::get (hb_user_data_key_t *key) +{ + hb_user_data_item_t item = {NULL }; + + return items.find (key, &item, lock) ? item.data : NULL; +} + + +/* hb_version */ + +/** + * hb_version: + * @major: (out): Library major version component. + * @minor: (out): Library minor version component. + * @micro: (out): Library micro version component. + * + * Returns library version as three integer components. + * + * Since: 0.9.2 + **/ +void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro) +{ + *major = HB_VERSION_MAJOR; + *minor = HB_VERSION_MINOR; + *micro = HB_VERSION_MICRO; +} + +/** + * hb_version_string: + * + * Returns library version as a string with three components. + * + * Return value: library version string. + * + * Since: 0.9.2 + **/ +const char * +hb_version_string (void) +{ + return HB_VERSION_STRING; +} + +/** + * hb_version_atleast: + * @major: + * @minor: + * @micro: + * + * + * + * Return value: + * + * Since: 0.9.30 + **/ +hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro) +{ + return HB_VERSION_ATLEAST (major, minor, micro); +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.h new file mode 100644 index 00000000000..1f742a2ef57 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.h @@ -0,0 +1,354 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_COMMON_H +#define HB_COMMON_H + +#ifndef HB_BEGIN_DECLS +# ifdef __cplusplus +# define HB_BEGIN_DECLS extern "C" { +# define HB_END_DECLS } +# else /* !__cplusplus */ +# define HB_BEGIN_DECLS +# define HB_END_DECLS +# endif /* !__cplusplus */ +#endif + +#if !defined (HB_DONT_DEFINE_STDINT) + +#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ + defined (_sgi) || defined (__sun) || defined (sun) || \ + defined (__digital__) || defined (__HP_cc) +# include +#elif defined (_AIX) +# include +/* VS 2010 (_MSC_VER 1600) has stdint.h */ +#elif defined (_MSC_VER) && _MSC_VER < 1600 +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +# include +#endif + +#endif + +HB_BEGIN_DECLS + + +typedef int hb_bool_t; + +typedef uint32_t hb_codepoint_t; +typedef int32_t hb_position_t; +typedef uint32_t hb_mask_t; + +typedef union _hb_var_int_t { + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + int8_t i8[4]; +} hb_var_int_t; + + +/* hb_tag_t */ + +typedef uint32_t hb_tag_t; + +#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4)))) +#define HB_UNTAG(tag) ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag)) + +#define HB_TAG_NONE HB_TAG(0,0,0,0) +#define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff) +#define HB_TAG_MAX_SIGNED HB_TAG(0x7f,0xff,0xff,0xff) + +/* len=-1 means str is NUL-terminated. */ +hb_tag_t +hb_tag_from_string (const char *str, int len); + +/* buf should have 4 bytes. */ +void +hb_tag_to_string (hb_tag_t tag, char *buf); + + +/* hb_direction_t */ + +typedef enum { + HB_DIRECTION_INVALID = 0, + HB_DIRECTION_LTR = 4, + HB_DIRECTION_RTL, + HB_DIRECTION_TTB, + HB_DIRECTION_BTT +} hb_direction_t; + +/* len=-1 means str is NUL-terminated */ +hb_direction_t +hb_direction_from_string (const char *str, int len); + +const char * +hb_direction_to_string (hb_direction_t direction); + +#define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) +/* Direction must be valid for the following */ +#define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4) +#define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6) +#define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4) +#define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5) +#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) + + +/* hb_language_t */ + +typedef const struct hb_language_impl_t *hb_language_t; + +/* len=-1 means str is NUL-terminated */ +hb_language_t +hb_language_from_string (const char *str, int len); + +const char * +hb_language_to_string (hb_language_t language); + +#define HB_LANGUAGE_INVALID ((hb_language_t) NULL) + +hb_language_t +hb_language_get_default (void); + + +/* hb_script_t */ + +/* http://unicode.org/iso15924/ */ +/* http://goo.gl/x9ilM */ +/* Unicode Character Database property: Script (sc) */ +typedef enum +{ + /*1.1*/ HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), + /*1.1*/ HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), + /*5.0*/ HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), + + /*1.1*/ HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), + /*1.1*/ HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), + /*1.1*/ HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), + /*1.1*/ HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), + /*1.1*/ HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), + /*1.1*/ HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), + /*1.1*/ HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), + /*1.1*/ HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), + /*1.1*/ HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), + /*1.1*/ HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), + /*1.1*/ HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), + /*1.1*/ HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), + /*1.1*/ HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), + /*1.1*/ HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), + /*1.1*/ HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), + /*1.1*/ HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), + /*1.1*/ HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), + /*1.1*/ HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), + /*1.1*/ HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), + /*1.1*/ HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), + /*1.1*/ HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), + /*1.1*/ HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), + + /*2.0*/ HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), + + /*3.0*/ HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), + /*3.0*/ HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), + /*3.0*/ HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), + /*3.0*/ HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), + /*3.0*/ HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), + /*3.0*/ HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), + /*3.0*/ HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), + /*3.0*/ HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), + /*3.0*/ HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), + /*3.0*/ HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), + /*3.0*/ HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), + /*3.0*/ HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), + /*3.0*/ HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), + /*3.0*/ HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), + + /*3.1*/ HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), + /*3.1*/ HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), + /*3.1*/ HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), + + /*3.2*/ HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), + /*3.2*/ HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), + /*3.2*/ HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), + /*3.2*/ HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), + + /*4.0*/ HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), + /*4.0*/ HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), + /*4.0*/ HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), + /*4.0*/ HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), + /*4.0*/ HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), + /*4.0*/ HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), + /*4.0*/ HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), + + /*4.1*/ HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), + /*4.1*/ HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), + /*4.1*/ HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), + /*4.1*/ HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), + /*4.1*/ HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), + /*4.1*/ HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), + /*4.1*/ HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), + /*4.1*/ HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), + + /*5.0*/ HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), + /*5.0*/ HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), + /*5.0*/ HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), + /*5.0*/ HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), + /*5.0*/ HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), + + /*5.1*/ HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), + /*5.1*/ HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), + /*5.1*/ HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), + /*5.1*/ HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), + /*5.1*/ HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), + /*5.1*/ HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), + /*5.1*/ HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), + /*5.1*/ HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), + /*5.1*/ HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), + /*5.1*/ HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), + /*5.1*/ HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), + + /*5.2*/ HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), + /*5.2*/ HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), + /*5.2*/ HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), + /*5.2*/ HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), + /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), + /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), + /*5.2*/ HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), + /*5.2*/ HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), + /*5.2*/ HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), + /*5.2*/ HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), + /*5.2*/ HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), + /*5.2*/ HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), + /*5.2*/ HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), + /*5.2*/ HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), + /*5.2*/ HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), + + /*6.0*/ HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), + /*6.0*/ HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), + /*6.0*/ HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), + + /*6.1*/ HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), + /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), + /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), + /*6.1*/ HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), + /*6.1*/ HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), + /*6.1*/ HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), + /*6.1*/ HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), + + /* + * Since: 0.9.30 + */ + /*7.0*/ HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), + /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), + /*7.0*/ HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), + /*7.0*/ HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), + /*7.0*/ HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), + /*7.0*/ HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), + /*7.0*/ HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), + /*7.0*/ HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), + /*7.0*/ HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), + /*7.0*/ HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), + /*7.0*/ HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), + /*7.0*/ HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), + /*7.0*/ HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), + /*7.0*/ HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), + /*7.0*/ HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), + /*7.0*/ HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), + /*7.0*/ HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), + /*7.0*/ HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), + /*7.0*/ HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), + /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), + /*7.0*/ HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), + /*7.0*/ HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), + /*7.0*/ HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), + + /*8.0*/ HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), + /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), + /*8.0*/ HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), + /*8.0*/ HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), + /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), + /*8.0*/ HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), + + /* No script set. */ + HB_SCRIPT_INVALID = HB_TAG_NONE, + + /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t + * without risking undefined behavior. Include both a signed and unsigned max, + * since technically enums are int, and indeed, hb_script_t ends up being signed. + * See this thread for technicalities: + * + * http://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + */ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ + +} hb_script_t; + + +/* Script functions */ + +hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag); + +/* sugar for tag_from_string() then script_from_iso15924_tag */ +/* len=-1 means s is NUL-terminated */ +hb_script_t +hb_script_from_string (const char *s, int len); + +hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script); + +hb_direction_t +hb_script_get_horizontal_direction (hb_script_t script); + + +/* User data */ + +typedef struct hb_user_data_key_t { + /*< private >*/ + char unused; +} hb_user_data_key_t; + +typedef void (*hb_destroy_func_t) (void *user_data); + + +HB_END_DECLS + +#endif /* HB_COMMON_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-coretext.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-coretext.cc new file mode 100644 index 00000000000..369b7365c2b --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-coretext.cc @@ -0,0 +1,1219 @@ +/* + * Copyright © 2012,2013 Mozilla Foundation. + * Copyright © 2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#define HB_SHAPER coretext +#define hb_coretext_shaper_face_data_t CGFont +#include "hb-shaper-impl-private.hh" + +#include "hb-coretext.h" + + +#ifndef HB_DEBUG_CORETEXT +#define HB_DEBUG_CORETEXT (HB_DEBUG+0) +#endif + + +static void +release_table_data (void *user_data) +{ + CFDataRef cf_data = reinterpret_cast (user_data); + CFRelease(cf_data); +} + +static hb_blob_t * +reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + CGFontRef cg_font = reinterpret_cast (user_data); + CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); + if (unlikely (!cf_data)) + return NULL; + + const char *data = reinterpret_cast (CFDataGetBytePtr (cf_data)); + const size_t length = CFDataGetLength (cf_data); + if (!data || !length) + return NULL; + + return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, + reinterpret_cast (const_cast<__CFData *> (cf_data)), + release_table_data); +} + +hb_face_t * +hb_coretext_face_create (CGFontRef cg_font) +{ + return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), (hb_destroy_func_t) CGFontRelease); +} + + +HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face) +HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) + + +/* + * shaper face data + */ + +static void +release_data (void *info, const void *data, size_t size) +{ + assert (hb_blob_get_length ((hb_blob_t *) info) == size && + hb_blob_get_data ((hb_blob_t *) info, NULL) == data); + + hb_blob_destroy ((hb_blob_t *) info); +} + +hb_coretext_shaper_face_data_t * +_hb_coretext_shaper_face_data_create (hb_face_t *face) +{ + hb_coretext_shaper_face_data_t *data = NULL; + + if (face->destroy == (hb_destroy_func_t) CGFontRelease) + { + data = CGFontRetain ((CGFontRef) face->user_data); + } + else + { + hb_blob_t *blob = hb_face_reference_blob (face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (CORETEXT, face, "Face has empty blob"); + + CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); + if (likely (provider)) + { + data = CGFontCreateWithDataProvider (provider); + CGDataProviderRelease (provider); + } + } + + if (unlikely (!data)) { + DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); + } + + return data; +} + +void +_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) +{ + CFRelease (data); +} + +/* + * Since: 0.9.10 + */ +CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face) +{ + if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; + hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + return face_data; +} + + +/* + * shaper font data + */ + +struct hb_coretext_shaper_font_data_t { + CTFontRef ct_font; + CGFloat x_mult, y_mult; /* From CT space to HB space. */ +}; + +hb_coretext_shaper_font_data_t * +_hb_coretext_shaper_font_data_create (hb_font_t *font) +{ + if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL; + + hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) calloc (1, sizeof (hb_coretext_shaper_font_data_t)); + if (unlikely (!data)) + return NULL; + + hb_face_t *face = font->face; + hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + + /* Choose a CoreText font size and calculate multipliers to convert to HarfBuzz space. */ + /* TODO: use upem instead of 36? */ + CGFloat font_size = 36.; /* Default... */ + /* No idea if the following is even a good idea. */ + if (font->y_ppem) + font_size = font->y_ppem; + + if (font_size < 0) + font_size = -font_size; + data->x_mult = (CGFloat) font->x_scale / font_size; + data->y_mult = (CGFloat) font->y_scale / font_size; + data->ct_font = CTFontCreateWithGraphicsFont (face_data, font_size, NULL, NULL); + if (unlikely (!data->ct_font)) { + DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed"); + free (data); + return NULL; + } + + return data; +} + +void +_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) +{ + CFRelease (data->ct_font); + free (data); +} + + +/* + * shaper shape_plan data + */ + +struct hb_coretext_shaper_shape_plan_data_t {}; + +hb_coretext_shaper_shape_plan_data_t * +_hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED) +{ + return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + +CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font) +{ + if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return NULL; + hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + return font_data->ct_font; +} + + +/* + * shaper + */ + +struct feature_record_t { + unsigned int feature; + unsigned int setting; +}; + +struct active_feature_t { + feature_record_t rec; + unsigned int order; + + static int cmp (const active_feature_t *a, const active_feature_t *b) { + return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : + 0; + } + bool operator== (const active_feature_t *f) { + return cmp (this, f) == 0; + } +}; + +struct feature_event_t { + unsigned int index; + bool start; + active_feature_t feature; + + static int cmp (const feature_event_t *a, const feature_event_t *b) { + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct range_record_t { + CTFontRef font; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + + +/* The following enum members are added in OS X 10.8. */ +#define kAltHalfWidthTextSelector 6 +#define kAltProportionalTextSelector 5 +#define kAlternateHorizKanaOffSelector 1 +#define kAlternateHorizKanaOnSelector 0 +#define kAlternateKanaType 34 +#define kAlternateVertKanaOffSelector 3 +#define kAlternateVertKanaOnSelector 2 +#define kCaseSensitiveLayoutOffSelector 1 +#define kCaseSensitiveLayoutOnSelector 0 +#define kCaseSensitiveLayoutType 33 +#define kCaseSensitiveSpacingOffSelector 3 +#define kCaseSensitiveSpacingOnSelector 2 +#define kContextualAlternatesOffSelector 1 +#define kContextualAlternatesOnSelector 0 +#define kContextualAlternatesType 36 +#define kContextualLigaturesOffSelector 19 +#define kContextualLigaturesOnSelector 18 +#define kContextualSwashAlternatesOffSelector 5 +#define kContextualSwashAlternatesOnSelector 4 +#define kDefaultLowerCaseSelector 0 +#define kDefaultUpperCaseSelector 0 +#define kHistoricalLigaturesOffSelector 21 +#define kHistoricalLigaturesOnSelector 20 +#define kHojoCharactersSelector 12 +#define kJIS2004CharactersSelector 11 +#define kLowerCasePetiteCapsSelector 2 +#define kLowerCaseSmallCapsSelector 1 +#define kLowerCaseType 37 +#define kMathematicalGreekOffSelector 11 +#define kMathematicalGreekOnSelector 10 +#define kNLCCharactersSelector 13 +#define kQuarterWidthTextSelector 4 +#define kScientificInferiorsSelector 4 +#define kStylisticAltEightOffSelector 17 +#define kStylisticAltEightOnSelector 16 +#define kStylisticAltEighteenOffSelector 37 +#define kStylisticAltEighteenOnSelector 36 +#define kStylisticAltElevenOffSelector 23 +#define kStylisticAltElevenOnSelector 22 +#define kStylisticAltFifteenOffSelector 31 +#define kStylisticAltFifteenOnSelector 30 +#define kStylisticAltFiveOffSelector 11 +#define kStylisticAltFiveOnSelector 10 +#define kStylisticAltFourOffSelector 9 +#define kStylisticAltFourOnSelector 8 +#define kStylisticAltFourteenOffSelector 29 +#define kStylisticAltFourteenOnSelector 28 +#define kStylisticAltNineOffSelector 19 +#define kStylisticAltNineOnSelector 18 +#define kStylisticAltNineteenOffSelector 39 +#define kStylisticAltNineteenOnSelector 38 +#define kStylisticAltOneOffSelector 3 +#define kStylisticAltOneOnSelector 2 +#define kStylisticAltSevenOffSelector 15 +#define kStylisticAltSevenOnSelector 14 +#define kStylisticAltSeventeenOffSelector 35 +#define kStylisticAltSeventeenOnSelector 34 +#define kStylisticAltSixOffSelector 13 +#define kStylisticAltSixOnSelector 12 +#define kStylisticAltSixteenOffSelector 33 +#define kStylisticAltSixteenOnSelector 32 +#define kStylisticAltTenOffSelector 21 +#define kStylisticAltTenOnSelector 20 +#define kStylisticAltThirteenOffSelector 27 +#define kStylisticAltThirteenOnSelector 26 +#define kStylisticAltThreeOffSelector 7 +#define kStylisticAltThreeOnSelector 6 +#define kStylisticAltTwelveOffSelector 25 +#define kStylisticAltTwelveOnSelector 24 +#define kStylisticAltTwentyOffSelector 41 +#define kStylisticAltTwentyOnSelector 40 +#define kStylisticAltTwoOffSelector 5 +#define kStylisticAltTwoOnSelector 4 +#define kStylisticAlternativesType 35 +#define kSwashAlternatesOffSelector 3 +#define kSwashAlternatesOnSelector 2 +#define kThirdWidthTextSelector 3 +#define kTraditionalNamesCharactersSelector 14 +#define kUpperCasePetiteCapsSelector 2 +#define kUpperCaseSmallCapsSelector 1 +#define kUpperCaseType 38 + +/* Table data courtesy of Apple. */ +static const struct feature_mapping_t { + FourCharCode otFeatureTag; + uint16_t aatFeatureType; + uint16_t selectorToEnable; + uint16_t selectorToDisable; +} feature_mappings[] = { + { 'c2pc', kUpperCaseType, kUpperCasePetiteCapsSelector, kDefaultUpperCaseSelector }, + { 'c2sc', kUpperCaseType, kUpperCaseSmallCapsSelector, kDefaultUpperCaseSelector }, + { 'calt', kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector }, + { 'case', kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector }, + { 'clig', kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector }, + { 'cpsp', kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector }, + { 'cswh', kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector }, + { 'dlig', kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector }, + { 'expt', kCharacterShapeType, kExpertCharactersSelector, 16 }, + { 'frac', kFractionsType, kDiagonalFractionsSelector, kNoFractionsSelector }, + { 'fwid', kTextSpacingType, kMonospacedTextSelector, 7 }, + { 'halt', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, + { 'hist', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, + { 'hkna', kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, }, + { 'hlig', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, + { 'hngl', kTransliterationType, kHanjaToHangulSelector, kNoTransliterationSelector }, + { 'hojo', kCharacterShapeType, kHojoCharactersSelector, 16 }, + { 'hwid', kTextSpacingType, kHalfWidthTextSelector, 7 }, + { 'ital', kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector }, + { 'jp04', kCharacterShapeType, kJIS2004CharactersSelector, 16 }, + { 'jp78', kCharacterShapeType, kJIS1978CharactersSelector, 16 }, + { 'jp83', kCharacterShapeType, kJIS1983CharactersSelector, 16 }, + { 'jp90', kCharacterShapeType, kJIS1990CharactersSelector, 16 }, + { 'liga', kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector }, + { 'lnum', kNumberCaseType, kUpperCaseNumbersSelector, 2 }, + { 'mgrk', kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector }, + { 'nlck', kCharacterShapeType, kNLCCharactersSelector, 16 }, + { 'onum', kNumberCaseType, kLowerCaseNumbersSelector, 2 }, + { 'ordn', kVerticalPositionType, kOrdinalsSelector, kNormalPositionSelector }, + { 'palt', kTextSpacingType, kAltProportionalTextSelector, 7 }, + { 'pcap', kLowerCaseType, kLowerCasePetiteCapsSelector, kDefaultLowerCaseSelector }, + { 'pkna', kTextSpacingType, kProportionalTextSelector, 7 }, + { 'pnum', kNumberSpacingType, kProportionalNumbersSelector, 4 }, + { 'pwid', kTextSpacingType, kProportionalTextSelector, 7 }, + { 'qwid', kTextSpacingType, kQuarterWidthTextSelector, 7 }, + { 'ruby', kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector }, + { 'sinf', kVerticalPositionType, kScientificInferiorsSelector, kNormalPositionSelector }, + { 'smcp', kLowerCaseType, kLowerCaseSmallCapsSelector, kDefaultLowerCaseSelector }, + { 'smpl', kCharacterShapeType, kSimplifiedCharactersSelector, 16 }, + { 'ss01', kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector }, + { 'ss02', kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector }, + { 'ss03', kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector }, + { 'ss04', kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector }, + { 'ss05', kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector }, + { 'ss06', kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector }, + { 'ss07', kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector }, + { 'ss08', kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector }, + { 'ss09', kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector }, + { 'ss10', kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector }, + { 'ss11', kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector }, + { 'ss12', kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector }, + { 'ss13', kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector }, + { 'ss14', kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector }, + { 'ss15', kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector }, + { 'ss16', kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector }, + { 'ss17', kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector }, + { 'ss18', kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector }, + { 'ss19', kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector }, + { 'ss20', kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector }, + { 'subs', kVerticalPositionType, kInferiorsSelector, kNormalPositionSelector }, + { 'sups', kVerticalPositionType, kSuperiorsSelector, kNormalPositionSelector }, + { 'swsh', kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector }, + { 'titl', kStyleOptionsType, kTitlingCapsSelector, kNoStyleOptionsSelector }, + { 'tnam', kCharacterShapeType, kTraditionalNamesCharactersSelector, 16 }, + { 'tnum', kNumberSpacingType, kMonospacedNumbersSelector, 4 }, + { 'trad', kCharacterShapeType, kTraditionalCharactersSelector, 16 }, + { 'twid', kTextSpacingType, kThirdWidthTextSelector, 7 }, + { 'unic', kLetterCaseType, 14, 15 }, + { 'valt', kTextSpacingType, kAltProportionalTextSelector, 7 }, + { 'vert', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, + { 'vhal', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, + { 'vkna', kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector }, + { 'vpal', kTextSpacingType, kAltProportionalTextSelector, 7 }, + { 'vrt2', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, + { 'zero', kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector }, +}; + +static int +_hb_feature_mapping_cmp (const void *key_, const void *entry_) +{ + unsigned int key = * (unsigned int *) key_; + const feature_mapping_t * entry = (const feature_mapping_t *) entry_; + return key < entry->otFeatureTag ? -1 : + key > entry->otFeatureTag ? 1 : + 0; +} + +hb_bool_t +_hb_coretext_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + + /* Attach marks to their bases, to match the 'ot' shaper. + * Adapted from hb-ot-shape:hb_form_clusters(). + * Note that this only makes us be closer to the 'ot' shaper, + * but by no means the same. For example, if there's + * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will + * continue pointing to B2 even though B2 was merged into B1's + * cluster... */ + { + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint))) + buffer->merge_clusters (i - 1, i + 1); + } + + hb_auto_array_t feature_records; + hb_auto_array_t range_records; + + /* + * Set up features. + * (copied + modified from code from hb-uniscribe.cc) + */ + if (num_features) + { + /* Sort features by start/end events. */ + hb_auto_array_t feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag, + feature_mappings, + ARRAY_LENGTH (feature_mappings), + sizeof (feature_mappings[0]), + _hb_feature_mapping_cmp); + if (!mapping) + continue; + + active_feature_t feature; + feature.rec.feature = mapping->aatFeatureType; + feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; + feature.order = i; + + feature_event_t *event; + + event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + active_feature_t feature; + feature.rec.feature = HB_TAG_NONE; + feature.rec.setting = 0; + feature.order = num_features + 1; + + feature_event_t *event = feature_events.push (); + if (unlikely (!event)) + goto fail_features; + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_auto_array_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.len; i++) + { + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + range_record_t *range = range_records.push (); + if (unlikely (!range)) + goto fail_features; + + if (active_features.len) + { + CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + /* TODO sort and resolve conflicting features? */ + /* active_features.qsort (); */ + for (unsigned int j = 0; j < active_features.len; j++) + { + CFStringRef keys[2] = { + kCTFontFeatureTypeIdentifierKey, + kCTFontFeatureSelectorIdentifierKey + }; + CFNumberRef values[2] = { + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; + CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) keys, + (const void **) values, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (values[0]); + CFRelease (values[1]); + + CFArrayAppendValue (features_array, dict); + CFRelease (dict); + + } + + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontFeatureSettingsAttribute, + (const void **) &features_array, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (features_array); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + + range->font = CTFontCreateCopyWithAttributes (font_data->ct_font, 0.0, NULL, font_desc); + CFRelease (font_desc); + } + else + { + range->font = NULL; + } + + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) { + active_feature_t *feature = active_features.push (); + if (unlikely (!feature)) + goto fail_features; + *feature = event->feature; + } else { + active_feature_t *feature = active_features.find (&event->feature); + if (feature) + active_features.remove (feature - active_features.array); + } + } + + if (!range_records.len) /* No active feature found. */ + goto fail_features; + } + else + { + fail_features: + num_features = 0; + } + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + +#define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ + Type *name = (Type *) scratch; \ + { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + if (unlikely (_consumed > scratch_size)) \ + { \ + on_no_room; \ + assert (0); \ + } \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } + + ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/); + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) { + hb_codepoint_t c = buffer->info[i].codepoint; + if (likely (c <= 0xFFFFu)) + pchars[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; + else { + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1)); + } + } + + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/); + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \ + ret = false; \ + goto fail; \ + } HB_STMT_END; + + bool ret = true; + CFStringRef string_ref = NULL; + CTLineRef line = NULL; + + if (0) + { +resize_and_retry: + DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); + /* string_ref uses the scratch-buffer for backing store, and line references + * string_ref (via attr_string). We must release those before resizing buffer. */ + assert (string_ref); + assert (line); + CFRelease (string_ref); + CFRelease (line); + string_ref = NULL; + line = NULL; + + /* Get previous start-of-scratch-area, that we use later for readjusting + * our existing scratch arrays. */ + unsigned int old_scratch_used; + hb_buffer_t::scratch_buffer_t *old_scratch; + old_scratch = buffer->get_scratch_buffer (&old_scratch_used); + old_scratch_used = scratch - old_scratch; + + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + FAIL ("Buffer resize failed"); + + /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the + * cleanest way to do without completely restructuring the rest of this shaper. */ + scratch = buffer->get_scratch_buffer (&scratch_size); + pchars = reinterpret_cast (((char *) scratch + ((char *) pchars - (char *) old_scratch))); + log_clusters = reinterpret_cast (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); + scratch += old_scratch_used; + scratch_size -= old_scratch_used; + } +retry: + { + string_ref = CFStringCreateWithCharactersNoCopy (NULL, + pchars, chars_len, + kCFAllocatorNull); + if (unlikely (!string_ref)) + FAIL ("CFStringCreateWithCharactersNoCopy failed"); + + /* Create an attributed string, populate it, and create a line from it, then release attributed string. */ + { + CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault, + chars_len); + if (unlikely (!attr_string)) + FAIL ("CFAttributedStringCreateMutable failed"); + CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + { + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTVerticalFormsAttributeName, kCFBooleanTrue); + } + + if (buffer->props.language) + { +/* What's the iOS equivalent of this check? + * The symbols was introduced in iOS 7.0. + * At any rate, our fallback is safe and works fine. */ +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +# define kCTLanguageAttributeName CFSTR ("NSLanguage") +#endif + CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, + hb_language_to_string (buffer->props.language), + kCFStringEncodingUTF8, + kCFAllocatorNull); + if (unlikely (!lang)) + FAIL ("CFStringCreateWithCStringNoCopy failed"); + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTLanguageAttributeName, lang); + CFRelease (lang); + } + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTFontAttributeName, font_data->ct_font); + + if (num_features) + { + unsigned int start = 0; + range_record_t *last_range = &range_records[0]; + for (unsigned int k = 0; k < chars_len; k++) + { + range_record_t *range = last_range; + while (log_clusters[k] < range->index_first) + range--; + while (log_clusters[k] > range->index_last) + range++; + if (range != last_range) + { + if (last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start), + kCTFontAttributeName, last_range->font); + + start = k; + } + + last_range = range; + } + if (start != chars_len && last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), + kCTFontAttributeName, last_range->font); + } + + int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; + CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); + CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, + (const void **) &level_number, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (unlikely (!options)) + FAIL ("CFDictionaryCreate failed"); + + CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); + CFRelease (options); + CFRelease (attr_string); + if (unlikely (!typesetter)) + FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed"); + + line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0)); + CFRelease (typesetter); + if (unlikely (!line)) + FAIL ("CTTypesetterCreateLine failed"); + } + + CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); + unsigned int num_runs = CFArrayGetCount (glyph_runs); + DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs); + + buffer->len = 0; + uint32_t status_and = ~0, status_or = 0; + double advances_so_far = 0; + /* For right-to-left runs, CoreText returns the glyphs positioned such that + * any trailing whitespace is to the left of (0,0). Adjust coordinate system + * to fix for that. Test with any RTL string with trailing spaces. + * https://code.google.com/p/chromium/issues/detail?id=469028 + */ + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + { + advances_so_far -= CTLineGetTrailingWhitespaceWidth (line); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + advances_so_far = -advances_so_far; + } + + const CFRange range_all = CFRangeMake (0, 0); + + for (unsigned int i = 0; i < num_runs; i++) + { + CTRunRef run = static_cast(CFArrayGetValueAtIndex (glyph_runs, i)); + CTRunStatus run_status = CTRunGetStatus (run); + status_or |= run_status; + status_and &= run_status; + DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); + double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + run_advance = -run_advance; + DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); + + /* CoreText does automatic font fallback (AKA "cascading") for characters + * not supported by the requested font, and provides no way to turn it off, + * so we must detect if the returned run uses a font other than the requested + * one and fill in the buffer with .notdef glyphs instead of random glyph + * indices from a different font. + */ + CFDictionaryRef attributes = CTRunGetAttributes (run); + CTFontRef run_ct_font = static_cast(CFDictionaryGetValue (attributes, kCTFontAttributeName)); + if (!CFEqual (run_ct_font, font_data->ct_font)) + { + /* The run doesn't use our main font instance. We have to figure out + * whether font fallback happened, or this is just CoreText giving us + * another CTFont using the same underlying CGFont. CoreText seems + * to do that in a variety of situations, one of which being vertical + * text, but also perhaps for caching reasons. + * + * First, see if it uses any of our subfonts created to set font features... + * + * Next, compare the CGFont to the one we used to create our fonts. + * Even this doesn't work all the time. + * + * Finally, we compare PS names, which I don't think are unique... + * + * Looks like if we really want to be sure here we have to modify the + * font to change the name table, similar to what we do in the uniscribe + * backend. + * + * However, even that wouldn't work if we were passed in the CGFont to + * begin with. + * + * Webkit uses a slightly different approach: it installs LastResort + * as fallback chain, and then checks PS name of used font against + * LastResort. That one is safe for any font except for LastResort, + * as opposed to ours, which can fail if we are using any uninstalled + * font that has the same name as an installed font. + * + * See: http://github.com/behdad/harfbuzz/pull/36 + */ + bool matched = false; + for (unsigned int i = 0; i < range_records.len; i++) + if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) + { + matched = true; + break; + } + if (!matched) + { + CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); + if (run_cg_font) + { + matched = CFEqual (run_cg_font, face_data); + CFRelease (run_cg_font); + } + } + if (!matched) + { + CFStringRef font_ps_name = CTFontCopyName (font_data->ct_font, kCTFontPostScriptNameKey); + CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); + CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); + CFRelease (run_ps_name); + CFRelease (font_ps_name); + if (result == kCFCompareEqualTo) + matched = true; + } + if (!matched) + { + CFRange range = CTRunGetStringRange (run); + DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", + range.location, range.location + range.length); + if (!buffer->ensure_inplace (buffer->len + range.length)) + goto resize_and_retry; + hb_glyph_info_t *info = buffer->info + buffer->len; + + hb_codepoint_t notdef = 0; + hb_direction_t dir = buffer->props.direction; + hb_position_t x_advance, y_advance, x_offset, y_offset; + hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance); + hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset); + hb_position_t advance = x_advance + y_advance; + x_offset = -x_offset; + y_offset = -y_offset; + + unsigned int old_len = buffer->len; + for (CFIndex j = range.location; j < range.location + range.length; j++) + { + UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); + if (hb_in_range (ch, 0xDC00u, 0xDFFFu) && range.location < j) + { + ch = CFStringGetCharacterAtIndex (string_ref, j - 1); + if (hb_in_range (ch, 0xD800u, 0xDBFFu)) + /* This is the second of a surrogate pair. Don't need .notdef + * for this one. */ + continue; + } + if (buffer->unicode->is_default_ignorable (ch)) + continue; + + info->codepoint = notdef; + info->cluster = log_clusters[j]; + + info->mask = advance; + info->var1.i32 = x_offset; + info->var2.i32 = y_offset; + + info++; + buffer->len++; + } + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + buffer->reverse_range (old_len, buffer->len); + advances_so_far += run_advance; + continue; + } + } + + unsigned int num_glyphs = CTRunGetGlyphCount (run); + if (num_glyphs == 0) + continue; + + if (!buffer->ensure_inplace (buffer->len + num_glyphs)) + goto resize_and_retry; + + hb_glyph_info_t *run_info = buffer->info + buffer->len; + + /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always + * succeed, and so copying data to our own buffer will be rare. Reports + * have it that this changed in OS X 10.10 Yosemite, and NULL is returned + * frequently. At any rate, we can test that codepath by setting USE_PTR + * to false. */ + +#define USE_PTR true + +#define SCRATCH_SAVE() \ + unsigned int scratch_size_saved = scratch_size; \ + hb_buffer_t::scratch_buffer_t *scratch_saved = scratch + +#define SCRATCH_RESTORE() \ + scratch_size = scratch_size_saved; \ + scratch = scratch_saved; + + { /* Setup glyphs */ + SCRATCH_SAVE(); + const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL; + if (!glyphs) { + ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); + CTRunGetGlyphs (run, range_all, glyph_buf); + glyphs = glyph_buf; + } + const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL; + if (!string_indices) { + ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); + CTRunGetStringIndices (run, range_all, index_buf); + string_indices = index_buf; + } + hb_glyph_info_t *info = run_info; + for (unsigned int j = 0; j < num_glyphs; j++) + { + info->codepoint = glyphs[j]; + info->cluster = log_clusters[string_indices[j]]; + info++; + } + SCRATCH_RESTORE(); + } + { + /* Setup positions. + * Note that CoreText does not return advances for glyphs. As such, + * for all but last glyph, we use the delta position to next glyph as + * advance (in the advance direction only), and for last glyph we set + * whatever is needed to make the whole run's advance add up. */ + SCRATCH_SAVE(); + const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL; + if (!positions) { + ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); + CTRunGetPositions (run, range_all, position_buf); + positions = position_buf; + } + hb_glyph_info_t *info = run_info; + CGFloat x_mult = font_data->x_mult, y_mult = font_data->y_mult; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult; + for (unsigned int j = 0; j < num_glyphs; j++) + { + double advance; + if (likely (j + 1 < num_glyphs)) + advance = positions[j + 1].x - positions[j].x; + else /* last glyph */ + advance = run_advance - (positions[j].x - positions[0].x); + info->mask = advance * x_mult; + info->var1.i32 = x_offset; + info->var2.i32 = positions[j].y * y_mult; + info++; + } + } + else + { + hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult; + for (unsigned int j = 0; j < num_glyphs; j++) + { + double advance; + if (likely (j + 1 < num_glyphs)) + advance = positions[j + 1].y - positions[j].y; + else /* last glyph */ + advance = run_advance - (positions[j].y - positions[0].y); + info->mask = advance * y_mult; + info->var1.i32 = positions[j].x * x_mult; + info->var2.i32 = y_offset; + info++; + } + } + SCRATCH_RESTORE(); + advances_so_far += run_advance; + } +#undef SCRATCH_RESTORE +#undef SCRATCH_SAVE +#undef USE_PTR +#undef ALLOCATE_ARRAY + + buffer->len += num_glyphs; + } + + /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, + * or if it does, it doesn't resepct it. So we get runs with wrong + * directions. As such, disable the assert... It wouldn't crash, but + * cursoring will be off... + * + * http://crbug.com/419769 + */ + if (0) + { + /* Make sure all runs had the expected direction. */ + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + assert (bool (status_and & kCTRunStatusRightToLeft) == backward); + assert (bool (status_or & kCTRunStatusRightToLeft) == backward); + } + + buffer->clear_positions (); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + for (unsigned int i = 0; i < count; i++) + { + pos->x_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + info++, pos++; + } + else + for (unsigned int i = 0; i < count; i++) + { + pos->y_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + info++, pos++; + } + + /* Fix up clusters so that we never return out-of-order indices; + * if core text has reordered glyphs, we'll merge them to the + * beginning of the reordered cluster. CoreText is nice enough + * to tell us whenever it has produced nonmonotonic results... + * Note that we assume the input clusters were nonmonotonic to + * begin with. + * + * This does *not* mean we'll form the same clusters as Uniscribe + * or the native OT backend, only that the cluster indices will be + * monotonic in the output buffer. */ + if (count > 1 && (status_or & kCTRunStatusNonMonotonic)) + { + hb_glyph_info_t *info = buffer->info; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) + { + unsigned int cluster = info[count - 1].cluster; + for (unsigned int i = count - 1; i > 0; i--) + { + cluster = MIN (cluster, info[i - 1].cluster); + info[i - 1].cluster = cluster; + } + } + else + { + unsigned int cluster = info[0].cluster; + for (unsigned int i = 1; i < count; i++) + { + cluster = MIN (cluster, info[i].cluster); + info[i].cluster = cluster; + } + } + } + } + +#undef FAIL + +fail: + if (string_ref) + CFRelease (string_ref); + if (line) + CFRelease (line); + + for (unsigned int i = 0; i < range_records.len; i++) + if (range_records[i].font) + CFRelease (range_records[i].font); + + return ret; +} + + +/* + * AAT shaper + */ + +HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, face) +HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, font) + + +/* + * shaper face data + */ + +struct hb_coretext_aat_shaper_face_data_t {}; + +hb_coretext_aat_shaper_face_data_t * +_hb_coretext_aat_shaper_face_data_create (hb_face_t *face) +{ + hb_blob_t *mort_blob = face->reference_table (HB_CORETEXT_TAG_MORT); + /* Umm, we just reference the table to check whether it exists. + * Maybe add better API for this? */ + if (!hb_blob_get_length (mort_blob)) + { + hb_blob_destroy (mort_blob); + mort_blob = face->reference_table (HB_CORETEXT_TAG_MORX); + if (!hb_blob_get_length (mort_blob)) + { + hb_blob_destroy (mort_blob); + return NULL; + } + } + hb_blob_destroy (mort_blob); + + return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL; +} + +void +_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper font data + */ + +struct hb_coretext_aat_shaper_font_data_t {}; + +hb_coretext_aat_shaper_font_data_t * +_hb_coretext_aat_shaper_font_data_create (hb_font_t *font) +{ + return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL; +} + +void +_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper shape_plan data + */ + +struct hb_coretext_aat_shaper_shape_plan_data_t {}; + +hb_coretext_aat_shaper_shape_plan_data_t * +_hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED) +{ + return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +hb_bool_t +_hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-coretext.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-coretext.h new file mode 100644 index 00000000000..25267bc9784 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-coretext.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2012 Mozilla Foundation. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Mozilla Author(s): Jonathan Kew + */ + +#ifndef HB_CORETEXT_H +#define HB_CORETEXT_H + +#include "hb.h" + +#include +#if TARGET_OS_IPHONE +# include +# include +#else +# include +#endif + +HB_BEGIN_DECLS + + +#define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') +#define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') + + +hb_face_t * +hb_coretext_face_create (CGFontRef cg_font); + + +CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face); + +CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_CORETEXT_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-deprecated.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-deprecated.h new file mode 100644 index 00000000000..14d435f0068 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-deprecated.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_DEPRECATED_H +#define HB_DEPRECATED_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +#ifndef HB_DISABLE_DEPRECATED + +#define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS + +#define HB_BUFFER_FLAGS_DEFAULT HB_BUFFER_FLAG_DEFAULT +#define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT HB_BUFFER_SERIALIZE_FLAG_DEFAULT + +#endif + +HB_END_DECLS + +#endif /* HB_DEPRECATED_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face-private.hh new file mode 100644 index 00000000000..c4266fff4f0 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face-private.hh @@ -0,0 +1,107 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FACE_PRIVATE_HH +#define HB_FACE_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-object-private.hh" +#include "hb-shaper-private.hh" +#include "hb-shape-plan-private.hh" + + +/* + * hb_face_t + */ + +struct hb_face_t { + hb_object_header_t header; + ASSERT_POD (); + + hb_bool_t immutable; + + hb_reference_table_func_t reference_table_func; + void *user_data; + hb_destroy_func_t destroy; + + unsigned int index; + mutable unsigned int upem; + mutable unsigned int num_glyphs; + + struct hb_shaper_data_t shaper_data; + + struct plan_node_t { + hb_shape_plan_t *shape_plan; + plan_node_t *next; + } *shape_plans; + + + inline hb_blob_t *reference_table (hb_tag_t tag) const + { + hb_blob_t *blob; + + if (unlikely (!reference_table_func)) + return hb_blob_get_empty (); + + blob = reference_table_func (/*XXX*/const_cast (this), tag, user_data); + if (unlikely (!blob)) + return hb_blob_get_empty (); + + return blob; + } + + inline HB_PURE_FUNC unsigned int get_upem (void) const + { + if (unlikely (!upem)) + load_upem (); + return upem; + } + + inline unsigned int get_num_glyphs (void) const + { + if (unlikely (num_glyphs == (unsigned int) -1)) + load_num_glyphs (); + return num_glyphs; + } + + private: + HB_INTERNAL void load_upem (void) const; + HB_INTERNAL void load_num_glyphs (void) const; +}; + +extern HB_INTERNAL const hb_face_t _hb_face_nil; + +#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS + + +#endif /* HB_FACE_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face.cc new file mode 100644 index 00000000000..b5019a122de --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face.cc @@ -0,0 +1,481 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-ot-layout-private.hh" + +#include "hb-font-private.hh" +#include "hb-open-file-private.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-maxp-table.hh" + +#include "hb-cache-private.hh" + +#include + + +/* + * hb_face_t + */ + +const hb_face_t _hb_face_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + NULL, /* reference_table_func */ + NULL, /* user_data */ + NULL, /* destroy */ + + 0, /* index */ + 1000, /* upem */ + 0, /* num_glyphs */ + + { +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + }, + + NULL, /* shape_plans */ +}; + + +/** + * hb_face_create_for_tables: + * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!reference_table_func || !(face = hb_object_create ())) { + if (destroy) + destroy (user_data); + return hb_face_get_empty (); + } + + face->reference_table_func = reference_table_func; + face->user_data = user_data; + face->destroy = destroy; + + face->upem = 0; + face->num_glyphs = (unsigned int) -1; + + return face; +} + + +typedef struct hb_face_for_data_closure_t { + hb_blob_t *blob; + unsigned int index; +} hb_face_for_data_closure_t; + +static hb_face_for_data_closure_t * +_hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) +{ + hb_face_for_data_closure_t *closure; + + closure = (hb_face_for_data_closure_t *) calloc (1, sizeof (hb_face_for_data_closure_t)); + if (unlikely (!closure)) + return NULL; + + closure->blob = blob; + closure->index = index; + + return closure; +} + +static void +_hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure) +{ + hb_blob_destroy (closure->blob); + free (closure); +} + +static hb_blob_t * +_hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data; + + if (tag == HB_TAG_NONE) + return hb_blob_reference (data->blob); + + const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer::lock_instance (data->blob); + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); + + const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag); + + hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length); + + return blob; +} + +/** + * hb_face_create: (Xconstructor) + * @blob: + * @index: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index) +{ + hb_face_t *face; + + if (unlikely (!blob)) + blob = hb_blob_get_empty (); + + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer::sanitize (hb_blob_reference (blob)), index); + + if (unlikely (!closure)) + return hb_face_get_empty (); + + face = hb_face_create_for_tables (_hb_face_for_data_reference_table, + closure, + (hb_destroy_func_t) _hb_face_for_data_closure_destroy); + + hb_face_set_index (face, index); + + return face; +} + +/** + * hb_face_get_empty: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_get_empty (void) +{ + return const_cast (&_hb_face_nil); +} + + +/** + * hb_face_reference: (skip) + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_reference (hb_face_t *face) +{ + return hb_object_reference (face); +} + +/** + * hb_face_destroy: (skip) + * @face: a face. + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_destroy (hb_face_t *face) +{ + if (!hb_object_destroy (face)) return; + + for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) + { + hb_face_t::plan_node_t *next = node->next; + hb_shape_plan_destroy (node->shape_plan); + free (node); + node = next; + } + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + + if (face->destroy) + face->destroy (face->user_data); + + free (face); +} + +/** + * hb_face_set_user_data: (skip) + * @face: a face. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_set_user_data (hb_face_t *face, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (face, key, data, destroy, replace); +} + +/** + * hb_face_get_user_data: (skip) + * @face: a face. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_face_get_user_data (hb_face_t *face, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (face, key); +} + +/** + * hb_face_make_immutable: + * @face: a face. + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_make_immutable (hb_face_t *face) +{ + if (unlikely (hb_object_is_inert (face))) + return; + + face->immutable = true; +} + +/** + * hb_face_is_immutable: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_is_immutable (hb_face_t *face) +{ + return face->immutable; +} + + +/** + * hb_face_reference_table: + * @face: a face. + * @tag: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_table (hb_face_t *face, + hb_tag_t tag) +{ + return face->reference_table (tag); +} + +/** + * hb_face_reference_blob: + * @face: a face. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_blob (hb_face_t *face) +{ + return face->reference_table (HB_TAG_NONE); +} + +/** + * hb_face_set_index: + * @face: a face. + * @index: + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_set_index (hb_face_t *face, + unsigned int index) +{ + if (face->immutable) + return; + + face->index = index; +} + +/** + * hb_face_get_index: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_index (hb_face_t *face) +{ + return face->index; +} + +/** + * hb_face_set_upem: + * @face: a face. + * @upem: + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem) +{ + if (face->immutable) + return; + + face->upem = upem; +} + +/** + * hb_face_get_upem: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_upem (hb_face_t *face) +{ + return face->get_upem (); +} + +void +hb_face_t::load_upem (void) const +{ + hb_blob_t *head_blob = OT::Sanitizer::sanitize (reference_table (HB_OT_TAG_head)); + const OT::head *head_table = OT::Sanitizer::lock_instance (head_blob); + upem = head_table->get_upem (); + hb_blob_destroy (head_blob); +} + +/** + * hb_face_set_glyph_count: + * @face: a face. + * @glyph_count: + * + * + * + * Since: 0.9.7 + **/ +void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count) +{ + if (face->immutable) + return; + + face->num_glyphs = glyph_count; +} + +/** + * hb_face_get_glyph_count: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +unsigned int +hb_face_get_glyph_count (hb_face_t *face) +{ + return face->get_num_glyphs (); +} + +void +hb_face_t::load_num_glyphs (void) const +{ + hb_blob_t *maxp_blob = OT::Sanitizer::sanitize (reference_table (HB_OT_TAG_maxp)); + const OT::maxp *maxp_table = OT::Sanitizer::lock_instance (maxp_blob); + num_glyphs = maxp_table->get_num_glyphs (); + hb_blob_destroy (maxp_blob); +} + + diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face.h new file mode 100644 index 00000000000..85807dda302 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-face.h @@ -0,0 +1,117 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_FACE_H +#define HB_FACE_H + +#include "hb-common.h" +#include "hb-blob.h" + +HB_BEGIN_DECLS + + +/* + * hb_face_t + */ + +typedef struct hb_face_t hb_face_t; + +hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index); + +typedef hb_blob_t * (*hb_reference_table_func_t) (hb_face_t *face, hb_tag_t tag, void *user_data); + +/* calls destroy() when not needing user_data anymore */ +hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy); + +hb_face_t * +hb_face_get_empty (void); + +hb_face_t * +hb_face_reference (hb_face_t *face); + +void +hb_face_destroy (hb_face_t *face); + +hb_bool_t +hb_face_set_user_data (hb_face_t *face, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +void * +hb_face_get_user_data (hb_face_t *face, + hb_user_data_key_t *key); + +void +hb_face_make_immutable (hb_face_t *face); + +hb_bool_t +hb_face_is_immutable (hb_face_t *face); + + +hb_blob_t * +hb_face_reference_table (hb_face_t *face, + hb_tag_t tag); + +hb_blob_t * +hb_face_reference_blob (hb_face_t *face); + +void +hb_face_set_index (hb_face_t *face, + unsigned int index); + +unsigned int +hb_face_get_index (hb_face_t *face); + +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem); + +unsigned int +hb_face_get_upem (hb_face_t *face); + +void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count); + +unsigned int +hb_face_get_glyph_count (hb_face_t *face); + + +HB_END_DECLS + +#endif /* HB_FACE_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-fallback-shape.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-fallback-shape.cc new file mode 100644 index 00000000000..8e1628be033 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-fallback-shape.cc @@ -0,0 +1,141 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#define HB_SHAPER fallback +#include "hb-shaper-impl-private.hh" + + +/* + * shaper face data + */ + +struct hb_fallback_shaper_face_data_t {}; + +hb_fallback_shaper_face_data_t * +_hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED) +{ + return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper font data + */ + +struct hb_fallback_shaper_font_data_t {}; + +hb_fallback_shaper_font_data_t * +_hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper shape_plan data + */ + +struct hb_fallback_shaper_shape_plan_data_t {}; + +hb_fallback_shaper_shape_plan_data_t * +_hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED) +{ + return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +hb_bool_t +_hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features HB_UNUSED, + unsigned int num_features HB_UNUSED) +{ + /* TODO + * + * - Apply fallback kern. + * - Handle Variation Selectors? + * - Apply normalization? + * + * This will make the fallback shaper into a dumb "TrueType" + * shaper which many people unfortunately still request. + */ + + hb_codepoint_t space; + bool has_space = font->get_glyph (' ', 0, &space); + + buffer->clear_positions (); + + hb_direction_t direction = buffer->props.direction; + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int i = 0; i < count; i++) + { + if (has_space && unicode->is_default_ignorable (info[i].codepoint)) { + info[i].codepoint = space; + pos[i].x_advance = 0; + pos[i].y_advance = 0; + continue; + } + font->get_glyph (info[i].codepoint, 0, &info[i].codepoint); + font->get_glyph_advance_for_direction (info[i].codepoint, + direction, + &pos[i].x_advance, + &pos[i].y_advance); + font->subtract_glyph_origin_for_direction (info[i].codepoint, + direction, + &pos[i].x_offset, + &pos[i].y_offset); + } + + if (HB_DIRECTION_IS_BACKWARD (direction)) + hb_buffer_reverse (buffer); + + return true; +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font-private.hh new file mode 100644 index 00000000000..253e71786f6 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font-private.hh @@ -0,0 +1,415 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FONT_PRIVATE_HH +#define HB_FONT_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-object-private.hh" +#include "hb-face-private.hh" +#include "hb-shaper-private.hh" + + + +/* + * hb_font_funcs_t + */ + +#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ + HB_FONT_FUNC_IMPLEMENT (glyph) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \ + HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ + HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ + HB_FONT_FUNC_IMPLEMENT (glyph_name) \ + HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ + /* ^--- Add new callbacks here */ + +struct hb_font_funcs_t { + hb_object_header_t header; + ASSERT_POD (); + + hb_bool_t immutable; + + /* Don't access these directly. Call hb_font_get_*() instead. */ + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } get; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) void *name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } user_data; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } destroy; +}; + + + +/* + * hb_font_t + */ + +struct hb_font_t { + hb_object_header_t header; + ASSERT_POD (); + + hb_bool_t immutable; + + hb_font_t *parent; + hb_face_t *face; + + int x_scale; + int y_scale; + + unsigned int x_ppem; + unsigned int y_ppem; + + hb_font_funcs_t *klass; + void *user_data; + hb_destroy_func_t destroy; + + struct hb_shaper_data_t shaper_data; + + + /* Convert from font-space to user-space */ + inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, this->x_scale); } + inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, this->y_scale); } + + /* Convert from parent-font user-space to our user-space */ + inline hb_position_t parent_scale_x_distance (hb_position_t v) { + if (unlikely (parent && parent->x_scale != x_scale)) + return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale); + return v; + } + inline hb_position_t parent_scale_y_distance (hb_position_t v) { + if (unlikely (parent && parent->y_scale != y_scale)) + return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale); + return v; + } + inline hb_position_t parent_scale_x_position (hb_position_t v) { + return parent_scale_x_distance (v); + } + inline hb_position_t parent_scale_y_position (hb_position_t v) { + return parent_scale_y_distance (v); + } + + inline void parent_scale_distance (hb_position_t *x, hb_position_t *y) { + *x = parent_scale_x_distance (*x); + *y = parent_scale_y_distance (*y); + } + inline void parent_scale_position (hb_position_t *x, hb_position_t *y) { + *x = parent_scale_x_position (*x); + *y = parent_scale_y_position (*y); + } + + + /* Public getters */ + + inline hb_bool_t has_glyph (hb_codepoint_t unicode) + { + hb_codepoint_t glyph; + return get_glyph (unicode, 0, &glyph); + } + + inline hb_bool_t get_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) + { + *glyph = 0; + return klass->get.glyph (this, user_data, + unicode, variation_selector, glyph, + klass->user_data.glyph); + } + + inline hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + { + return klass->get.glyph_h_advance (this, user_data, + glyph, + klass->user_data.glyph_h_advance); + } + + inline hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) + { + return klass->get.glyph_v_advance (this, user_data, + glyph, + klass->user_data.glyph_v_advance); + } + + inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.glyph_h_origin (this, user_data, + glyph, x, y, + klass->user_data.glyph_h_origin); + } + + inline hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.glyph_v_origin (this, user_data, + glyph, x, y, + klass->user_data.glyph_v_origin); + } + + inline hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) + { + return klass->get.glyph_h_kerning (this, user_data, + left_glyph, right_glyph, + klass->user_data.glyph_h_kerning); + } + + inline hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) + { + return klass->get.glyph_v_kerning (this, user_data, + top_glyph, bottom_glyph, + klass->user_data.glyph_v_kerning); + } + + inline hb_bool_t get_glyph_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.glyph_extents (this, user_data, + glyph, + extents, + klass->user_data.glyph_extents); + } + + inline hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.glyph_contour_point (this, user_data, + glyph, point_index, + x, y, + klass->user_data.glyph_contour_point); + } + + inline hb_bool_t get_glyph_name (hb_codepoint_t glyph, + char *name, unsigned int size) + { + if (size) *name = '\0'; + return klass->get.glyph_name (this, user_data, + glyph, + name, size, + klass->user_data.glyph_name); + } + + inline hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + *glyph = 0; + if (len == -1) len = strlen (name); + return klass->get.glyph_from_name (this, user_data, + name, len, + glyph, + klass->user_data.glyph_from_name); + } + + + /* A bit higher-level, and with fallback */ + + inline void get_glyph_advance_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { + *x = get_glyph_h_advance (glyph); + *y = 0; + } else { + *x = 0; + *y = get_glyph_v_advance (glyph); + } + } + + /* Internal only */ + inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = get_glyph_h_advance (glyph) / 2; + + /* TODO use font_metrics.ascent */ + *y = y_scale; + } + + inline void get_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + { + if (!get_glyph_h_origin (glyph, x, y) && + get_glyph_v_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x -= dx; *y -= dy; + } + } + else + { + if (!get_glyph_v_origin (glyph, x, y) && + get_glyph_h_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x += dx; *y += dy; + } + } + } + + inline void add_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + + inline void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + + inline void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { + *x = get_glyph_h_kerning (first_glyph, second_glyph); + *y = 0; + } else { + *x = 0; + *y = get_glyph_v_kerning (first_glyph, second_glyph); + } + } + + inline hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) + { + hb_bool_t ret = get_glyph_extents (glyph, extents); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing); + + return ret; + } + + inline hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, x, y); + + return ret; + } + + /* Generates gidDDD if glyph has no name. */ + inline void + glyph_to_string (hb_codepoint_t glyph, + char *s, unsigned int size) + { + if (get_glyph_name (glyph, s, size)) return; + + if (size && snprintf (s, size, "gid%u", glyph) < 0) + *s = '\0'; + } + + /* Parses gidDDD and uniUUUU strings automatically. */ + inline hb_bool_t + glyph_from_string (const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + if (get_glyph_from_name (s, len, glyph)) return true; + + if (len == -1) len = strlen (s); + + /* Straight glyph index. */ + if (hb_codepoint_parse (s, len, 10, glyph)) + return true; + + if (len > 3) + { + /* gidDDD syntax for glyph indices. */ + if (0 == strncmp (s, "gid", 3) && + hb_codepoint_parse (s + 3, len - 3, 10, glyph)) + return true; + + /* uniUUUU and other Unicode character indices. */ + hb_codepoint_t unichar; + if (0 == strncmp (s, "uni", 3) && + hb_codepoint_parse (s + 3, len - 3, 16, &unichar) && + get_glyph (unichar, 0, glyph)) + return true; + } + + return false; + } + + private: + inline hb_position_t em_scale (int16_t v, int scale) { return (hb_position_t) (v * (int64_t) scale / face->get_upem ()); } +}; + +#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS + + +#endif /* HB_FONT_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font.cc new file mode 100644 index 00000000000..fdf871f15ff --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font.cc @@ -0,0 +1,1266 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-ot-layout-private.hh" + +#include "hb-font-private.hh" +#include "hb-open-file-private.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-maxp-table.hh" + +#include "hb-cache-private.hh" + +#include + + +/* + * hb_font_funcs_t + */ + +static hb_bool_t +hb_font_get_glyph_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + if (font->parent) + return font->parent->get_glyph (unicode, variation_selector, glyph); + + *glyph = 0; + return false; +} + +static hb_position_t +hb_font_get_glyph_h_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + if (font->parent) + return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); + + return font->x_scale; +} + +static hb_position_t +hb_font_get_glyph_v_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + if (font->parent) + return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); + + return font->y_scale; +} + +static hb_bool_t +hb_font_get_glyph_h_origin_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + if (font->parent) { + hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; + } + + *x = *y = 0; + return false; +} + +static hb_bool_t +hb_font_get_glyph_v_origin_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + if (font->parent) { + hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; + } + + *x = *y = 0; + return false; +} + +static hb_position_t +hb_font_get_glyph_h_kerning_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + if (font->parent) + return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); + + return 0; +} + +static hb_position_t +hb_font_get_glyph_v_kerning_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph, + void *user_data HB_UNUSED) +{ + if (font->parent) + return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); + + return 0; +} + +static hb_bool_t +hb_font_get_glyph_extents_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + if (font->parent) { + hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents); + if (ret) { + font->parent_scale_position (&extents->x_bearing, &extents->y_bearing); + font->parent_scale_distance (&extents->width, &extents->height); + } + return ret; + } + + memset (extents, 0, sizeof (*extents)); + return false; +} + +static hb_bool_t +hb_font_get_glyph_contour_point_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + if (font->parent) { + hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; + } + + *x = *y = 0; + return false; +} + +static hb_bool_t +hb_font_get_glyph_name_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + if (font->parent) + return font->parent->get_glyph_name (glyph, name, size); + + if (size) *name = '\0'; + return false; +} + +static hb_bool_t +hb_font_get_glyph_from_name_nil (hb_font_t *font, + void *font_data HB_UNUSED, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + if (font->parent) + return font->parent->get_glyph_from_name (name, len, glyph); + + *glyph = 0; + return false; +} + + +static const hb_font_funcs_t _hb_font_funcs_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } +}; + + +/** + * hb_font_funcs_create: (Xconstructor) + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_create (void) +{ + hb_font_funcs_t *ffuncs; + + if (!(ffuncs = hb_object_create ())) + return hb_font_funcs_get_empty (); + + ffuncs->get = _hb_font_funcs_nil.get; + + return ffuncs; +} + +/** + * hb_font_funcs_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_get_empty (void) +{ + return const_cast (&_hb_font_funcs_nil); +} + +/** + * hb_font_funcs_reference: (skip) + * @ffuncs: font functions. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs) +{ + return hb_object_reference (ffuncs); +} + +/** + * hb_font_funcs_destroy: (skip) + * @ffuncs: font functions. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) +{ + if (!hb_object_destroy (ffuncs)) return; + +#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy.name) \ + ffuncs->destroy.name (ffuncs->user_data.name); + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + free (ffuncs); +} + +/** + * hb_font_funcs_set_user_data: (skip) + * @ffuncs: font functions. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (ffuncs, key, data, destroy, replace); +} + +/** + * hb_font_funcs_get_user_data: (skip) + * @ffuncs: font functions. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (ffuncs, key); +} + + +/** + * hb_font_funcs_make_immutable: + * @ffuncs: font functions. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) +{ + if (unlikely (hb_object_is_inert (ffuncs))) + return; + + ffuncs->immutable = true; +} + +/** + * hb_font_funcs_is_immutable: + * @ffuncs: font functions. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) +{ + return ffuncs->immutable; +} + + +#define HB_FONT_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ + hb_font_get_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (ffuncs->immutable) { \ + if (destroy) \ + destroy (user_data); \ + return; \ + } \ + \ + if (ffuncs->destroy.name) \ + ffuncs->destroy.name (ffuncs->user_data.name); \ + \ + if (func) { \ + ffuncs->get.name = func; \ + ffuncs->user_data.name = user_data; \ + ffuncs->destroy.name = destroy; \ + } else { \ + ffuncs->get.name = hb_font_get_##name##_nil; \ + ffuncs->user_data.name = NULL; \ + ffuncs->destroy.name = NULL; \ + } \ +} + +HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + +/* Public getters */ + +/** + * hb_font_get_glyph: + * @font: a font. + * @unicode: + * @variation_selector: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) +{ + return font->get_glyph (unicode, variation_selector, glyph); +} + +/** + * hb_font_get_glyph_h_advance: + * @font: a font. + * @glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->get_glyph_h_advance (glyph); +} + +/** + * hb_font_get_glyph_v_advance: + * @font: a font. + * @glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->get_glyph_v_advance (glyph); +} + +/** + * hb_font_get_glyph_h_origin: + * @font: a font. + * @glyph: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_h_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_v_origin: + * @font: a font. + * @glyph: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_v_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_h_kerning: + * @font: a font. + * @left_glyph: + * @right_glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) +{ + return font->get_glyph_h_kerning (left_glyph, right_glyph); +} + +/** + * hb_font_get_glyph_v_kerning: + * @font: a font. + * @top_glyph: + * @bottom_glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) +{ + return font->get_glyph_v_kerning (top_glyph, bottom_glyph); +} + +/** + * hb_font_get_glyph_extents: + * @font: a font. + * @glyph: + * @extents: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) +{ + return font->get_glyph_extents (glyph, extents); +} + +/** + * hb_font_get_glyph_contour_point: + * @font: a font. + * @glyph: + * @point_index: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_contour_point (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_contour_point (glyph, point_index, x, y); +} + +/** + * hb_font_get_glyph_name: + * @font: a font. + * @glyph: + * @name: (array length=size): + * @size: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size) +{ + return font->get_glyph_name (glyph, name, size); +} + +/** + * hb_font_get_glyph_from_name: + * @font: a font. + * @name: (array length=len): + * @len: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) +{ + return font->get_glyph_from_name (name, len, glyph); +} + + +/* A bit higher-level, and with fallback */ + +/** + * hb_font_get_glyph_advance_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_advance_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_add_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->add_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_subtract_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->subtract_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_kerning_for_direction: + * @font: a font. + * @first_glyph: + * @second_glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_extents_for_origin: + * @font: a font. + * @glyph: + * @direction: + * @extents: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) +{ + return font->get_glyph_extents_for_origin (glyph, direction, extents); +} + +/** + * hb_font_get_glyph_contour_point_for_origin: + * @font: a font. + * @glyph: + * @point_index: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y); +} + +/* Generates gidDDD if glyph has no name. */ +/** + * hb_font_glyph_to_string: + * @font: a font. + * @glyph: + * @s: (array length=size): + * @size: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size) +{ + font->glyph_to_string (glyph, s, size); +} + +/* Parses gidDDD and uniUUUU strings automatically. */ +/** + * hb_font_glyph_from_string: + * @font: a font. + * @s: (array length=len) (element-type uint8_t): + * @len: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) +{ + return font->glyph_from_string (s, len, glyph); +} + + +/* + * hb_font_t + */ + +/** + * hb_font_create: (Xconstructor) + * @face: a face. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create (hb_face_t *face) +{ + hb_font_t *font; + + if (unlikely (!face)) + face = hb_face_get_empty (); + if (!(font = hb_object_create ())) + return hb_font_get_empty (); + + hb_face_make_immutable (face); + font->face = hb_face_reference (face); + font->klass = hb_font_funcs_get_empty (); + + font->x_scale = font->y_scale = hb_face_get_upem (face); + + return font; +} + +/** + * hb_font_create_sub_font: + * @parent: parent font. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create_sub_font (hb_font_t *parent) +{ + if (unlikely (!parent)) + parent = hb_font_get_empty (); + + hb_font_t *font = hb_font_create (parent->face); + + if (unlikely (hb_object_is_inert (font))) + return font; + + font->parent = hb_font_reference (parent); + + font->x_scale = parent->x_scale; + font->y_scale = parent->y_scale; + font->x_ppem = parent->x_ppem; + font->y_ppem = parent->y_ppem; + + return font; +} + +/** + * hb_font_get_empty: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_empty (void) +{ + static const hb_font_t _hb_font_nil = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + NULL, /* parent */ + const_cast (&_hb_face_nil), + + 0, /* x_scale */ + 0, /* y_scale */ + + 0, /* x_ppem */ + 0, /* y_ppem */ + + const_cast (&_hb_font_funcs_nil), /* klass */ + NULL, /* user_data */ + NULL, /* destroy */ + + { +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + }; + + return const_cast (&_hb_font_nil); +} + +/** + * hb_font_reference: (skip) + * @font: a font. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_reference (hb_font_t *font) +{ + return hb_object_reference (font); +} + +/** + * hb_font_destroy: (skip) + * @font: a font. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_destroy (hb_font_t *font) +{ + if (!hb_object_destroy (font)) return; + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, font); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + + if (font->destroy) + font->destroy (font->user_data); + + hb_font_destroy (font->parent); + hb_face_destroy (font->face); + hb_font_funcs_destroy (font->klass); + + free (font); +} + +/** + * hb_font_set_user_data: (skip) + * @font: a font. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_set_user_data (hb_font_t *font, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (font, key, data, destroy, replace); +} + +/** + * hb_font_get_user_data: (skip) + * @font: a font. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_font_get_user_data (hb_font_t *font, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (font, key); +} + +/** + * hb_font_make_immutable: + * @font: a font. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_make_immutable (hb_font_t *font) +{ + if (unlikely (hb_object_is_inert (font))) + return; + + if (font->parent) + hb_font_make_immutable (font->parent); + + font->immutable = true; +} + +/** + * hb_font_is_immutable: + * @font: a font. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_is_immutable (hb_font_t *font) +{ + return font->immutable; +} + +/** + * hb_font_set_parent: + * @font: a font. + * @parent: new parent. + * + * Sets parent font of @font. + * + * Since: 1.0.5 + **/ +void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent) +{ + if (font->immutable) + return; + + if (!parent) + parent = hb_font_get_empty (); + + hb_font_t *old = font->parent; + + font->parent = hb_font_reference (parent); + + hb_font_destroy (old); +} + +/** + * hb_font_get_parent: + * @font: a font. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_parent (hb_font_t *font) +{ + return font->parent; +} + +/** + * hb_font_get_face: + * @font: a font. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_font_get_face (hb_font_t *font) +{ + return font->face; +} + + +/** + * hb_font_set_funcs: + * @font: a font. + * @klass: (closure font_data) (destroy destroy) (scope notified): + * @font_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy) +{ + if (font->immutable) { + if (destroy) + destroy (font_data); + return; + } + + if (font->destroy) + font->destroy (font->user_data); + + if (!klass) + klass = hb_font_funcs_get_empty (); + + hb_font_funcs_reference (klass); + hb_font_funcs_destroy (font->klass); + font->klass = klass; + font->user_data = font_data; + font->destroy = destroy; +} + +/** + * hb_font_set_funcs_data: + * @font: a font. + * @font_data: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy) +{ + /* Destroy user_data? */ + if (font->immutable) { + if (destroy) + destroy (font_data); + return; + } + + if (font->destroy) + font->destroy (font->user_data); + + font->user_data = font_data; + font->destroy = destroy; +} + + +/** + * hb_font_set_scale: + * @font: a font. + * @x_scale: + * @y_scale: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale) +{ + if (font->immutable) + return; + + font->x_scale = x_scale; + font->y_scale = y_scale; +} + +/** + * hb_font_get_scale: + * @font: a font. + * @x_scale: (out): + * @y_scale: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale) +{ + if (x_scale) *x_scale = font->x_scale; + if (y_scale) *y_scale = font->y_scale; +} + +/** + * hb_font_set_ppem: + * @font: a font. + * @x_ppem: + * @y_ppem: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem) +{ + if (font->immutable) + return; + + font->x_ppem = x_ppem; + font->y_ppem = y_ppem; +} + +/** + * hb_font_get_ppem: + * @font: a font. + * @x_ppem: (out): + * @y_ppem: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem) +{ + if (x_ppem) *x_ppem = font->x_ppem; + if (y_ppem) *y_ppem = font->y_ppem; +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font.h new file mode 100644 index 00000000000..21049ca4a29 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-font.h @@ -0,0 +1,512 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_FONT_H +#define HB_FONT_H + +#include "hb-common.h" +#include "hb-face.h" + +HB_BEGIN_DECLS + + +typedef struct hb_font_t hb_font_t; + + +/* + * hb_font_funcs_t + */ + +typedef struct hb_font_funcs_t hb_font_funcs_t; + +hb_font_funcs_t * +hb_font_funcs_create (void); + +hb_font_funcs_t * +hb_font_funcs_get_empty (void); + +hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs); + +void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs); + +hb_bool_t +hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +void * +hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key); + + +void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs); + +hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); + + +/* glyph extents */ + +/* Note that height is negative in coordinate systems that grow up. */ +typedef struct hb_glyph_extents_t +{ + hb_position_t x_bearing; /* left side of glyph from origin. */ + hb_position_t y_bearing; /* top side of glyph from origin. */ + hb_position_t width; /* distance from left to right side. */ + hb_position_t height; /* distance from top to bottom side. */ +} hb_glyph_extents_t; + + +/* func types */ + +typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data); + + +typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + void *user_data); +typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t; +typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t; + +typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y, + void *user_data); +typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; +typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; + +typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + void *user_data); +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; + + +typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data); +typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y, + void *user_data); + + +typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data); +typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data); + + +/* func setters */ + +/** + * hb_font_funcs_set_glyph_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_advance_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_h_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advance_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_origin_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_h_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_origin_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_kerning_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_kerning_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_extents_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_contour_point_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_contour_point_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_name_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_from_name_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_from_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + + +/* func dispatch */ + +hb_bool_t +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph); + +hb_position_t +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph); +hb_position_t +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph); + +hb_bool_t +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y); +hb_bool_t +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y); + +hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); +hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); + +hb_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents); + +hb_bool_t +hb_font_get_glyph_contour_point (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y); + +hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size); +hb_bool_t +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + + +/* high-level funcs, with fallback */ + +void +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +void +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +void +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +void +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents); + +hb_bool_t +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +/* Generates gidDDD if glyph has no name. */ +void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size); +/* Parses gidDDD and uniUUUU strings automatically. */ +hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + + +/* + * hb_font_t + */ + +/* Fonts are very light-weight objects */ + +hb_font_t * +hb_font_create (hb_face_t *face); + +hb_font_t * +hb_font_create_sub_font (hb_font_t *parent); + +hb_font_t * +hb_font_get_empty (void); + +hb_font_t * +hb_font_reference (hb_font_t *font); + +void +hb_font_destroy (hb_font_t *font); + +hb_bool_t +hb_font_set_user_data (hb_font_t *font, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +void * +hb_font_get_user_data (hb_font_t *font, + hb_user_data_key_t *key); + +void +hb_font_make_immutable (hb_font_t *font); + +hb_bool_t +hb_font_is_immutable (hb_font_t *font); + +void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent); + +hb_font_t * +hb_font_get_parent (hb_font_t *font); + +hb_face_t * +hb_font_get_face (hb_font_t *font); + + +void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy); + +/* Be *very* careful with this function! */ +void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy); + + +void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale); + +void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale); + +/* + * A zero value means "no hinting in that direction" + */ +void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem); + +void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem); + + +HB_END_DECLS + +#endif /* HB_FONT_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ft.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ft.cc new file mode 100644 index 00000000000..5a8eb2f1a57 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ft.cc @@ -0,0 +1,650 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * Copyright © 2015 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-private.hh" + +#include "hb-ft.h" + +#include "hb-font-private.hh" + +#include FT_ADVANCES_H +#include FT_TRUETYPE_TABLES_H + + + +#ifndef HB_DEBUG_FT +#define HB_DEBUG_FT (HB_DEBUG+0) +#endif + + +/* TODO: + * + * In general, this file does a fine job of what it's supposed to do. + * There are, however, things that need more work: + * + * - I remember seeing FT_Get_Advance() without the NO_HINTING flag to be buggy. + * Have not investigated. + * + * - FreeType works in 26.6 mode. Clients can decide to use that mode, and everything + * would work fine. However, we also abuse this API for performing in font-space, + * but don't pass the correct flags to FreeType. We just abuse the no-hinting mode + * for that, such that no rounding etc happens. As such, we don't set ppem, and + * pass NO_HINTING as load_flags. Would be much better to use NO_SCALE, and scale + * ourselves, like we do in uniscribe, etc. + * + * - We don't handle / allow for emboldening / obliqueing. + * + * - In the future, we should add constructors to create fonts in font space? + * + * - FT_Load_Glyph() is exteremely costly. Do something about it? + */ + + +struct hb_ft_font_t +{ + FT_Face ft_face; + int load_flags; + bool unref; /* Whether to destroy ft_face when done. */ +}; + +static hb_ft_font_t * +_hb_ft_font_create (FT_Face ft_face, bool unref) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t)); + + if (unlikely (!ft_font)) + return NULL; + + ft_font->ft_face = ft_face; + ft_font->unref = unref; + + ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; + + return ft_font; +} + +static void +_hb_ft_font_destroy (hb_ft_font_t *ft_font) +{ + if (ft_font->unref) + FT_Done_Face (ft_font->ft_face); + + free (ft_font); +} + +/** + * hb_ft_font_set_load_flags: + * @font: + * @load_flags: + * + * + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) +{ + if (font->immutable) + return; + + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + ft_font->load_flags = load_flags; +} + +/** + * hb_ft_font_get_load_flags: + * @font: + * + * + * + * Return value: + * Since: 1.0.5 + **/ +int +hb_ft_font_get_load_flags (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return 0; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->load_flags; +} + +FT_Face +hb_ft_font_get_face (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return NULL; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->ft_face; +} + + + +static hb_bool_t +hb_ft_get_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) + +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + unsigned int g; + + if (likely (!variation_selector)) + g = FT_Get_Char_Index (ft_font->ft_face, unicode); + else + g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector); + + if (unlikely (!g)) + return false; + + *glyph = g; + return true; +} + +static hb_position_t +hb_ft_get_glyph_h_advance (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Fixed v; + + if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags, &v))) + return 0; + + if (font->x_scale < 0) + v = -v; + + return (v + (1<<9)) >> 10; +} + +static hb_position_t +hb_ft_get_glyph_v_advance (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Fixed v; + + if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) + return 0; + + if (font->y_scale < 0) + v = -v; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + return (-v + (1<<9)) >> 10; +} + +static hb_bool_t +hb_ft_get_glyph_h_origin (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x HB_UNUSED, + hb_position_t *y HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* We always work in the horizontal coordinates. */ + return true; +} + +static hb_bool_t +hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; + *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); + + if (font->x_scale < 0) + *x = -*x; + if (font->y_scale < 0) + *y = -*y; + + return true; +} + +static hb_position_t +hb_ft_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Vector kerningv; + + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) + return 0; + + return kerningv.x; +} + +static hb_position_t +hb_ft_get_glyph_v_kerning (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph HB_UNUSED, + hb_codepoint_t bottom_glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* FreeType API doesn't support vertical kerning */ + return 0; +} + +static hb_bool_t +hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + extents->x_bearing = ft_face->glyph->metrics.horiBearingX; + extents->y_bearing = ft_face->glyph->metrics.horiBearingY; + extents->width = ft_face->glyph->metrics.width; + extents->height = -ft_face->glyph->metrics.height; + return true; +} + +static hb_bool_t +hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) + return false; + + if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) + return false; + + *x = ft_face->glyph->outline.points[point_index].x; + *y = ft_face->glyph->outline.points[point_index].y; + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + + hb_bool_t ret = !FT_Get_Glyph_Name (ft_font->ft_face, glyph, name, size); + if (ret && (size && !*name)) + ret = false; + + return ret; +} + +static hb_bool_t +hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + if (len < 0) + *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name); + else { + /* Make a nul-terminated version. */ + char buf[128]; + len = MIN (len, (int) sizeof (buf) - 1); + strncpy (buf, name, len); + buf[len] = '\0'; + *glyph = FT_Get_Name_Index (ft_face, buf); + } + + if (*glyph == 0) + { + /* Check whether the given name was actually the name of glyph 0. */ + char buf[128]; + if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) && + len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len)) + return true; + } + + return *glyph != 0; +} + + +static void +_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref) +{ + static const hb_font_funcs_t ft_ffuncs = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_ft_get_##name, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + }; + + hb_font_set_funcs (font, + const_cast (&ft_ffuncs), + _hb_ft_font_create (ft_face, unref), + (hb_destroy_func_t) _hb_ft_font_destroy); +} + + +static hb_blob_t * +reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + FT_Face ft_face = (FT_Face) user_data; + FT_Byte *buffer; + FT_ULong length = 0; + FT_Error error; + + /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length); + if (error) + return NULL; + + buffer = (FT_Byte *) malloc (length); + if (buffer == NULL) + return NULL; + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); + if (error) + return NULL; + + return hb_blob_create ((const char *) buffer, length, + HB_MEMORY_MODE_WRITABLE, + buffer, free); +} + +/** + * hb_ft_face_create: + * @ft_face: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Return value: (transfer full): + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (ft_face->stream->read == NULL) { + hb_blob_t *blob; + + blob = hb_blob_create ((const char *) ft_face->stream->base, + (unsigned int) ft_face->stream->size, + HB_MEMORY_MODE_READONLY, + ft_face, destroy); + face = hb_face_create (blob, ft_face->face_index); + hb_blob_destroy (blob); + } else { + face = hb_face_create_for_tables (reference_table, ft_face, destroy); + } + + hb_face_set_index (face, ft_face->face_index); + hb_face_set_upem (face, ft_face->units_per_EM); + + return face; +} + +/** + * hb_ft_face_create_referenced: + * @ft_face: + * + * + * + * Return value: (transfer full): + * Since: 0.9.38 + **/ +hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_face_create (ft_face, (hb_destroy_func_t) FT_Done_Face); +} + +static void +hb_ft_face_finalize (FT_Face ft_face) +{ + hb_face_destroy ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_face_create_cached: + * @ft_face: + * + * + * + * Return value: (transfer full): + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face) +{ + if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) + { + if (ft_face->generic.finalizer) + ft_face->generic.finalizer (ft_face); + + ft_face->generic.data = hb_ft_face_create (ft_face, NULL); + ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; + } + + return hb_face_reference ((hb_face_t *) ft_face->generic.data); +} + + +/** + * hb_ft_font_create: + * @ft_face: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Return value: (transfer full): + * Since: 0.9.2 + **/ +hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_font_t *font; + hb_face_t *face; + + face = hb_ft_face_create (ft_face, destroy); + font = hb_font_create (face); + hb_face_destroy (face); + _hb_ft_font_set_funcs (font, ft_face, false); + hb_font_set_scale (font, + (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16), + (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16)); +#if 0 /* hb-ft works in no-hinting model */ + hb_font_set_ppem (font, + ft_face->size->metrics.x_ppem, + ft_face->size->metrics.y_ppem); +#endif + + return font; +} + +/** + * hb_ft_font_create_referenced: + * @ft_face: + * + * + * + * Return value: (transfer full): + * Since: 0.9.38 + **/ +hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_font_create (ft_face, (hb_destroy_func_t) FT_Done_Face); +} + + +/* Thread-safe, lock-free, FT_Library */ + +static FT_Library ft_library; + +#ifdef HB_USE_ATEXIT +static +void free_ft_library (void) +{ + FT_Done_FreeType (ft_library); +} +#endif + +static FT_Library +get_ft_library (void) +{ +retry: + FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library); + + if (unlikely (!library)) + { + /* Not found; allocate one. */ + if (FT_Init_FreeType (&library)) + return NULL; + + if (!hb_atomic_ptr_cmpexch (&ft_library, NULL, library)) { + FT_Done_FreeType (library); + goto retry; + } + +#ifdef HB_USE_ATEXIT + atexit (free_ft_library); /* First person registers atexit() callback. */ +#endif + } + + return library; +} + +static void +_release_blob (FT_Face ft_face) +{ + hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); +} + +void +hb_ft_font_set_funcs (hb_font_t *font) +{ + hb_blob_t *blob = hb_face_reference_blob (font->face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (FT, font, "Font face has empty blob"); + + FT_Face ft_face = NULL; + FT_Error err = FT_New_Memory_Face (get_ft_library (), + (const FT_Byte *) blob_data, + blob_length, + hb_face_get_index (font->face), + &ft_face); + + if (unlikely (err)) { + hb_blob_destroy (blob); + DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed"); + return; + } + + FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); + + FT_Set_Char_Size (ft_face, + abs (font->x_scale), abs (font->y_scale), + 0, 0); +#if 0 + font->x_ppem * 72 * 64 / font->x_scale, + font->y_ppem * 72 * 64 / font->y_scale); +#endif + if (font->x_scale < 0 || font->y_scale < 0) + { + FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0, + 0, font->y_scale < 0 ? -1 : +1}; + FT_Set_Transform (ft_face, &matrix, NULL); + } + + ft_face->generic.data = blob; + ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; + + _hb_ft_font_set_funcs (font, ft_face, true); + hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ft.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ft.h new file mode 100644 index 00000000000..368c317a083 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ft.h @@ -0,0 +1,126 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2015 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FT_H +#define HB_FT_H + +#include "hb.h" + +#include +#include FT_FREETYPE_H + +HB_BEGIN_DECLS + +/* + * Note: FreeType is not thread-safe. + * Hence, these functions are not either. + */ + +/* + * hb-face from ft-face. + */ + +/* This one creates a new hb-face for given ft-face. + * When the returned hb-face is destroyed, the destroy + * callback is called (if not NULL), with the ft-face passed + * to it. + * + * The client is responsible to make sure that ft-face is + * destroyed after hb-face is destroyed. + * + * Most often you don't want this function. You should use either + * hb_ft_face_create_cached(), or hb_ft_face_create_referenced(). + * In particular, if you are going to pass NULL as destroy, you + * probably should use (the more recent) hb_ft_face_create_referenced() + * instead. + */ +hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* This version is like hb_ft_face_create(), except that it caches + * the hb-face using the generic pointer of the ft-face. This means + * that subsequent calls to this function with the same ft-face will + * return the same hb-face (correctly referenced). + * + * Client is still responsible for making sure that ft-face is destroyed + * after hb-face is. + */ +hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face); + +/* This version is like hb_ft_face_create(), except that it calls + * FT_Reference_Face() on ft-face, as such keeping ft-face alive + * as long as the hb-face is. + * + * This is the most convenient version to use. Use it unless you have + * very good reasons not to. + */ +hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face); + + +/* + * hb-font from ft-face. + */ + +/* + * Note: + * + * Set face size on ft-face before creating hb-font from it. + * Otherwise hb-ft would NOT pick up the font size correctly. + */ + +/* See notes on hb_ft_face_create(). Same issues re lifecycle-management + * apply here. Use hb_ft_font_create_referenced() if you can. */ +hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* See notes on hb_ft_face_create_referenced() re lifecycle-management + * issues. */ +hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face); + +FT_Face +hb_ft_font_get_face (hb_font_t *font); + +void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags); + +int +hb_ft_font_get_load_flags (hb_font_t *font); + +/* Makes an hb_font_t use FreeType internally to implement font functions. */ +void +hb_ft_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_FT_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-mutex-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-mutex-private.hh new file mode 100644 index 00000000000..a69d8869ec1 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-mutex-private.hh @@ -0,0 +1,141 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MUTEX_PRIVATE_HH +#define HB_MUTEX_PRIVATE_HH + +#include "hb-private.hh" + + +/* mutex */ + +/* We need external help for these */ + +#if defined(HB_MUTEX_IMPL_INIT) \ + && defined(hb_mutex_impl_init) \ + && defined(hb_mutex_impl_lock) \ + && defined(hb_mutex_impl_unlock) \ + && defined(hb_mutex_impl_finish) + +/* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ + + +#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) + +#include +typedef CRITICAL_SECTION hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT {0} +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) +#else +#define hb_mutex_impl_init(M) InitializeCriticalSection (M) +#endif +#define hb_mutex_impl_lock(M) EnterCriticalSection (M) +#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M) +#define hb_mutex_impl_finish(M) DeleteCriticalSection (M) + + +#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include +typedef pthread_mutex_t hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER +#define hb_mutex_impl_init(M) pthread_mutex_init (M, NULL) +#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) +#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) +#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) + + +#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) + +#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) +# include +# define HB_SCHED_YIELD() sched_yield () +#else +# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END +#endif + +/* This actually is not a totally awful implementation. */ +typedef volatile int hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT 0 +#define hb_mutex_impl_init(M) *(M) = 0 +#define hb_mutex_impl_lock(M) HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END +#define hb_mutex_impl_unlock(M) __sync_lock_release (M) +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END + + +#elif !defined(HB_NO_MT) + +#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) +# include +# define HB_SCHED_YIELD() sched_yield () +#else +# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END +#endif + +#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */ +typedef volatile int hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT 0 +#define hb_mutex_impl_init(M) *(M) = 0 +#define hb_mutex_impl_lock(M) HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END +#define hb_mutex_impl_unlock(M) (*(M))--; +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END + + +#else /* HB_NO_MT */ + +typedef int hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT 0 +#define hb_mutex_impl_init(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_lock(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_unlock(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END + + +#endif + + +#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT} + +struct hb_mutex_t +{ + /* TODO Add tracing. */ + + hb_mutex_impl_t m; + + inline void init (void) { hb_mutex_impl_init (&m); } + inline void lock (void) { hb_mutex_impl_lock (&m); } + inline void unlock (void) { hb_mutex_impl_unlock (&m); } + inline void finish (void) { hb_mutex_impl_finish (&m); } +}; + + +#endif /* HB_MUTEX_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-object-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-object-private.hh new file mode 100644 index 00000000000..f2bb23ee7d1 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-object-private.hh @@ -0,0 +1,202 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OBJECT_PRIVATE_HH +#define HB_OBJECT_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-atomic-private.hh" +#include "hb-mutex-private.hh" + + +/* Debug */ + +#ifndef HB_DEBUG_OBJECT +#define HB_DEBUG_OBJECT (HB_DEBUG+0) +#endif + + +/* reference_count */ + +#define HB_REFERENCE_COUNT_INERT_VALUE -1 +#define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD +#define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT(HB_REFERENCE_COUNT_INERT_VALUE)} + +struct hb_reference_count_t +{ + hb_atomic_int_t ref_count; + + inline void init (int v) { ref_count.set_unsafe (v); } + inline int get_unsafe (void) const { return ref_count.get_unsafe (); } + inline int inc (void) { return ref_count.inc (); } + inline int dec (void) { return ref_count.dec (); } + inline void finish (void) { ref_count.set_unsafe (HB_REFERENCE_COUNT_POISON_VALUE); } + + inline bool is_inert (void) const { return ref_count.get_unsafe () == HB_REFERENCE_COUNT_INERT_VALUE; } + inline bool is_valid (void) const { return ref_count.get_unsafe () > 0; } +}; + + +/* user_data */ + +#define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT} +struct hb_user_data_array_t +{ + struct hb_user_data_item_t { + hb_user_data_key_t *key; + void *data; + hb_destroy_func_t destroy; + + inline bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; } + inline bool operator == (hb_user_data_item_t &other) const { return key == other.key; } + + void finish (void) { if (destroy) destroy (data); } + }; + + hb_mutex_t lock; + hb_lockable_set_t items; + + inline void init (void) { lock.init (); items.init (); } + + HB_INTERNAL bool set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + HB_INTERNAL void *get (hb_user_data_key_t *key); + + inline void finish (void) { items.finish (lock); lock.finish (); } +}; + + +/* object_header */ + +struct hb_object_header_t +{ + hb_reference_count_t ref_count; + hb_user_data_array_t user_data; + +#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, HB_USER_DATA_ARRAY_INIT} + + private: + ASSERT_POD (); +}; + + +/* object */ + +template +static inline void hb_object_trace (const Type *obj, const char *function) +{ + DEBUG_MSG (OBJECT, (void *) obj, + "%s refcount=%d", + function, + obj ? obj->header.ref_count.get_unsafe () : 0); +} + +template +static inline Type *hb_object_create (void) +{ + Type *obj = (Type *) calloc (1, sizeof (Type)); + + if (unlikely (!obj)) + return obj; + + hb_object_init (obj); + hb_object_trace (obj, HB_FUNC); + return obj; +} +template +static inline void hb_object_init (Type *obj) +{ + obj->header.ref_count.init (1); + obj->header.user_data.init (); +} +template +static inline bool hb_object_is_inert (const Type *obj) +{ + return unlikely (obj->header.ref_count.is_inert ()); +} +template +static inline bool hb_object_is_valid (const Type *obj) +{ + return likely (obj->header.ref_count.is_valid ()); +} +template +static inline Type *hb_object_reference (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || hb_object_is_inert (obj))) + return obj; + assert (hb_object_is_valid (obj)); + obj->header.ref_count.inc (); + return obj; +} +template +static inline bool hb_object_destroy (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || hb_object_is_inert (obj))) + return false; + assert (hb_object_is_valid (obj)); + if (obj->header.ref_count.dec () != 1) + return false; + + obj->header.ref_count.finish (); /* Do this before user_data */ + obj->header.user_data.finish (); + return true; +} +template +static inline bool hb_object_set_user_data (Type *obj, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (unlikely (!obj || hb_object_is_inert (obj))) + return false; + assert (hb_object_is_valid (obj)); + return obj->header.user_data.set (key, data, destroy, replace); +} + +template +static inline void *hb_object_get_user_data (Type *obj, + hb_user_data_key_t *key) +{ + if (unlikely (!obj || hb_object_is_inert (obj))) + return NULL; + assert (hb_object_is_valid (obj)); + return obj->header.user_data.get (key); +} + + +#endif /* HB_OBJECT_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-open-file-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-open-file-private.hh new file mode 100644 index 00000000000..2d3b13c5236 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-open-file-private.hh @@ -0,0 +1,268 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_FILE_PRIVATE_HH +#define HB_OPEN_FILE_PRIVATE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * + * The OpenType Font File + * + */ + + +/* + * Organization of an OpenType Font + */ + +struct OpenTypeFontFile; +struct OffsetTable; +struct TTCHeader; + + +typedef struct TableRecord +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + Tag tag; /* 4-byte identifier. */ + CheckSum checkSum; /* CheckSum for this table. */ + ULONG offset; /* Offset from beginning of TrueType font + * file. */ + ULONG length; /* Length of this table. */ + public: + DEFINE_SIZE_STATIC (16); +} OpenTypeTable; + +typedef struct OffsetTable +{ + friend struct OpenTypeFontFile; + + inline unsigned int get_table_count (void) const + { return numTables; } + inline const TableRecord& get_table (unsigned int i) const + { + if (unlikely (i >= numTables)) return Null(TableRecord); + return tables[i]; + } + inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const + { + Tag t; + t.set (tag); + unsigned int count = numTables; + for (unsigned int i = 0; i < count; i++) + { + if (t == tables[i].tag) + { + if (table_index) *table_index = i; + return true; + } + } + if (table_index) *table_index = Index::NOT_FOUND_INDEX; + return false; + } + inline const TableRecord& get_table_by_tag (hb_tag_t tag) const + { + unsigned int table_index; + find_table_index (tag, &table_index); + return get_table (table_index); + } + + public: + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables)); + } + + protected: + Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ + USHORT numTables; /* Number of tables. */ + USHORT searchRangeZ; /* (Maximum power of 2 <= numTables) x 16 */ + USHORT entrySelectorZ; /* Log2(maximum power of 2 <= numTables). */ + USHORT rangeShiftZ; /* NumTables x 16-searchRange. */ + TableRecord tables[VAR]; /* TableRecord entries. numTables items */ + public: + DEFINE_SIZE_ARRAY (12, tables); +} OpenTypeFontFace; + + +/* + * TrueType Collections + */ + +struct TTCHeaderVersion1 +{ + friend struct TTCHeader; + + inline unsigned int get_face_count (void) const { return table.len; } + inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (table.sanitize (c, this)); + } + + protected: + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion version; /* Version of the TTC Header (1.0), + * 0x00010000u */ + ArrayOf, ULONG> + table; /* Array of offsets to the OffsetTable for each font + * from the beginning of the file */ + public: + DEFINE_SIZE_ARRAY (12, table); +}; + +struct TTCHeader +{ + friend struct OpenTypeFontFile; + + private: + + inline unsigned int get_face_count (void) const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face_count (); + default:return 0; + } + } + inline const OpenTypeFontFace& get_face (unsigned int i) const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face (i); + default:return Null(OpenTypeFontFace); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.version.sanitize (c))) return_trace (false); + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return_trace (u.version1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + struct { + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion version; /* Version of the TTC Header (1.0 or 2.0), + * 0x00010000u or 0x00020000u */ + } header; + TTCHeaderVersion1 version1; + } u; +}; + + +/* + * OpenType Font File + */ + +struct OpenTypeFontFile +{ + static const hb_tag_t tableTag = HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */ + + static const hb_tag_t CFFTag = HB_TAG ('O','T','T','O'); /* OpenType with Postscript outlines */ + static const hb_tag_t TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ); /* OpenType with TrueType outlines */ + static const hb_tag_t TTCTag = HB_TAG ('t','t','c','f'); /* TrueType Collection */ + static const hb_tag_t TrueTag = HB_TAG ('t','r','u','e'); /* Obsolete Apple TrueType */ + static const hb_tag_t Typ1Tag = HB_TAG ('t','y','p','1'); /* Obsolete Apple Type1 font in SFNT container */ + + inline hb_tag_t get_tag (void) const { return u.tag; } + + inline unsigned int get_face_count (void) const + { + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return 1; + case TTCTag: return u.ttcHeader.get_face_count (); + default: return 0; + } + } + inline const OpenTypeFontFace& get_face (unsigned int i) const + { + switch (u.tag) { + /* Note: for non-collection SFNT data we ignore index. This is because + * Apple dfont container is a container of SFNT's. So each SFNT is a + * non-TTC, but the index is more than zero. */ + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return u.fontFace; + case TTCTag: return u.ttcHeader.get_face (i); + default: return Null(OpenTypeFontFace); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.tag.sanitize (c))) return_trace (false); + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); + case TTCTag: return_trace (u.ttcHeader.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + Tag tag; /* 4-byte identifier. */ + OpenTypeFontFace fontFace; + TTCHeader ttcHeader; + } u; + public: + DEFINE_SIZE_UNION (4, tag); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_FILE_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-open-type-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-open-type-private.hh new file mode 100644 index 00000000000..6b3520cf071 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-open-type-private.hh @@ -0,0 +1,1046 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_TYPE_PRIVATE_HH +#define HB_OPEN_TYPE_PRIVATE_HH + +#include "hb-private.hh" + + +namespace OT { + + + +/* + * Casts + */ + +/* Cast to struct T, reference to reference */ +template +static inline const Type& CastR(const TObject &X) +{ return reinterpret_cast (X); } +template +static inline Type& CastR(TObject &X) +{ return reinterpret_cast (X); } + +/* Cast to struct T, pointer to pointer */ +template +static inline const Type* CastP(const TObject *X) +{ return reinterpret_cast (X); } +template +static inline Type* CastP(TObject *X) +{ return reinterpret_cast (X); } + +/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory + * location pointed to by P plus Ofs bytes. */ +template +static inline const Type& StructAtOffset(const void *P, unsigned int offset) +{ return * reinterpret_cast ((const char *) P + offset); } +template +static inline Type& StructAtOffset(void *P, unsigned int offset) +{ return * reinterpret_cast ((char *) P + offset); } + +/* StructAfter(X) returns the struct T& that is placed after X. + * Works with X of variable size also. X must implement get_size() */ +template +static inline const Type& StructAfter(const TObject &X) +{ return StructAtOffset(&X, X.get_size()); } +template +static inline Type& StructAfter(TObject &X) +{ return StructAtOffset(&X, X.get_size()); } + + + +/* + * Size checking + */ + +/* Check _assertion in a method environment */ +#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ + inline void _instance_assertion_on_line_##_line (void) const \ + { \ + ASSERT_STATIC (_assertion); \ + ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \ + } +# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) +# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) + +/* Check that _code compiles in a method environment */ +#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ + inline void _compiles_assertion_on_line_##_line (void) const \ + { _code; } +# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) +# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) + + +#define DEFINE_SIZE_STATIC(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \ + static const unsigned int static_size = (size); \ + static const unsigned int min_size = (size) + +/* Size signifying variable-sized array */ +#define VAR 1 + +#define DEFINE_SIZE_UNION(size, _member) \ + DEFINE_INSTANCE_ASSERTION (this->u._member.static_size == (size)); \ + static const unsigned int min_size = (size) + +#define DEFINE_SIZE_MIN(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \ + static const unsigned int min_size = (size) + +#define DEFINE_SIZE_ARRAY(size, array) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \ + DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \ + static const unsigned int min_size = (size) + +#define DEFINE_SIZE_ARRAY2(size, array1, array2) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \ + DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \ + static const unsigned int min_size = (size) + + + +/* + * Null objects + */ + +/* Global nul-content Null pool. Enlarge as necessary. */ +/* TODO This really should be a extern HB_INTERNAL and defined somewhere... */ +static const void *_NullPool[(256+8) / sizeof (void *)]; + +/* Generic nul-content Null objects. */ +template +static inline const Type& Null (void) { + ASSERT_STATIC (sizeof (Type) <= sizeof (_NullPool)); + return *CastP (_NullPool); +} + +/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */ +#define DEFINE_NULL_DATA(Type, data) \ +static const char _Null##Type[sizeof (Type) + 1] = data; /* +1 is for nul-termination in data */ \ +template <> \ +/*static*/ inline const Type& Null (void) { \ + return *CastP (_Null##Type); \ +} /* The following line really exists such that we end in a place needing semicolon */ \ +ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type)) + +/* Accessor macro. */ +#define Null(Type) Null() + + +/* + * Dispatch + */ + +template +struct hb_dispatch_context_t +{ + static const unsigned int max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template + inline bool may_dispatch (const T *obj, const F *format) { return true; } + static return_t no_dispatch_return_value (void) { return Context::default_return_value (); } +}; + + +/* + * Sanitize + */ + +#ifndef HB_DEBUG_SANITIZE +#define HB_DEBUG_SANITIZE (HB_DEBUG+0) +#endif + + +#define TRACE_SANITIZE(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + ""); + +/* This limits sanitizing time on really broken fonts. */ +#ifndef HB_SANITIZE_MAX_EDITS +#define HB_SANITIZE_MAX_EDITS 100 +#endif + +struct hb_sanitize_context_t : + hb_dispatch_context_t +{ + inline hb_sanitize_context_t (void) : + debug_depth (0), + start (NULL), end (NULL), + writable (false), edit_count (0), + blob (NULL) {} + + inline const char *get_name (void) { return "SANITIZE"; } + template + inline bool may_dispatch (const T *obj, const F *format) + { return format->sanitize (this); } + template + inline return_t dispatch (const T &obj) { return obj.sanitize (this); } + static return_t default_return_value (void) { return true; } + static return_t no_dispatch_return_value (void) { return false; } + bool stop_sublookup_iteration (const return_t r) const { return !r; } + + inline void init (hb_blob_t *b) + { + this->blob = hb_blob_reference (b); + this->writable = false; + } + + inline void start_processing (void) + { + this->start = hb_blob_get_data (this->blob, NULL); + this->end = this->start + hb_blob_get_length (this->blob); + assert (this->start <= this->end); /* Must not overflow. */ + this->edit_count = 0; + this->debug_depth = 0; + + DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + } + + inline void end_processing (void) + { + DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, + "end [%p..%p] %u edit requests", + this->start, this->end, this->edit_count); + + hb_blob_destroy (this->blob); + this->blob = NULL; + this->start = this->end = NULL; + } + + inline bool check_range (const void *base, unsigned int len) const + { + const char *p = (const char *) base; + bool ok = this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const + { + const char *p = (const char *) base; + bool overflows = _hb_unsigned_int_mul_overflows (len, record_size); + unsigned int array_size = record_size * len; + bool ok = !overflows && this->check_range (base, array_size); + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s", + p, p + (record_size * len), record_size, len, (unsigned int) array_size, + this->start, this->end, + overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + template + inline bool check_struct (const Type *obj) const + { + return likely (this->check_range (obj, obj->min_size)); + } + + inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED) + { + if (this->edit_count >= HB_SANITIZE_MAX_EDITS) + return false; + + const char *p = (const char *) base; + this->edit_count++; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", + this->edit_count, + p, p + len, len, + this->start, this->end, + this->writable ? "GRANTED" : "DENIED"); + + return this->writable; + } + + template + inline bool try_set (const Type *obj, const ValueType &v) { + if (this->may_edit (obj, obj->static_size)) { + const_cast (obj)->set (v); + return true; + } + return false; + } + + mutable unsigned int debug_depth; + const char *start, *end; + bool writable; + unsigned int edit_count; + hb_blob_t *blob; +}; + + + +/* Template to sanitize an object. */ +template +struct Sanitizer +{ + static hb_blob_t *sanitize (hb_blob_t *blob) { + hb_sanitize_context_t c[1]; + bool sane; + + /* TODO is_sane() stuff */ + + c->init (blob); + + retry: + DEBUG_MSG_FUNC (SANITIZE, c->start, "start"); + + c->start_processing (); + + if (unlikely (!c->start)) { + c->end_processing (); + return blob; + } + + Type *t = CastP (const_cast (c->start)); + + sane = t->sanitize (c); + if (sane) { + if (c->edit_count) { + DEBUG_MSG_FUNC (SANITIZE, c->start, "passed first round with %d edits; going for second round", c->edit_count); + + /* sanitize again to ensure no toe-stepping */ + c->edit_count = 0; + sane = t->sanitize (c); + if (c->edit_count) { + DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count); + sane = false; + } + } + } else { + unsigned int edit_count = c->edit_count; + if (edit_count && !c->writable) { + c->start = hb_blob_get_data_writable (blob, NULL); + c->end = c->start + hb_blob_get_length (blob); + + if (c->start) { + c->writable = true; + /* ok, we made it writable by relocating. try again */ + DEBUG_MSG_FUNC (SANITIZE, c->start, "retry"); + goto retry; + } + } + } + + c->end_processing (); + + DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED"); + if (sane) + return blob; + else { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + static const Type* lock_instance (hb_blob_t *blob) { + hb_blob_make_immutable (blob); + const char *base = hb_blob_get_data (blob, NULL); + return unlikely (!base) ? &Null(Type) : CastP (base); + } +}; + + + +/* + * Serialize + */ + +#ifndef HB_DEBUG_SERIALIZE +#define HB_DEBUG_SERIALIZE (HB_DEBUG+0) +#endif + + +#define TRACE_SERIALIZE(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ + ""); + + +struct hb_serialize_context_t +{ + inline hb_serialize_context_t (void *start, unsigned int size) + { + this->start = (char *) start; + this->end = this->start + size; + + this->ran_out_of_room = false; + this->head = this->start; + this->debug_depth = 0; + } + + template + inline Type *start_serialize (void) + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + + return start_embed (); + } + + inline void end_serialize (void) + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, + "end [%p..%p] serialized %d bytes; %s", + this->start, this->end, + (int) (this->head - this->start), + this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room"); + + } + + template + inline Type *copy (void) + { + assert (!this->ran_out_of_room); + unsigned int len = this->head - this->start; + void *p = malloc (len); + if (p) + memcpy (p, this->start, len); + return reinterpret_cast (p); + } + + template + inline Type *allocate_size (unsigned int size) + { + if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) { + this->ran_out_of_room = true; + return NULL; + } + memset (this->head, 0, size); + char *ret = this->head; + this->head += size; + return reinterpret_cast (ret); + } + + template + inline Type *allocate_min (void) + { + return this->allocate_size (Type::min_size); + } + + template + inline Type *start_embed (void) + { + Type *ret = reinterpret_cast (this->head); + return ret; + } + + template + inline Type *embed (const Type &obj) + { + unsigned int size = obj.get_size (); + Type *ret = this->allocate_size (size); + if (unlikely (!ret)) return NULL; + memcpy (ret, obj, size); + return ret; + } + + template + inline Type *extend_min (Type &obj) + { + unsigned int size = obj.min_size; + assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); + if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return NULL; + return reinterpret_cast (&obj); + } + + template + inline Type *extend (Type &obj) + { + unsigned int size = obj.get_size (); + assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); + if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return NULL; + return reinterpret_cast (&obj); + } + + inline void truncate (void *head) + { + assert (this->start < head && head <= this->head); + this->head = (char *) head; + } + + unsigned int debug_depth; + char *start, *end, *head; + bool ran_out_of_room; +}; + +template +struct Supplier +{ + inline Supplier (const Type *array, unsigned int len_) + { + head = array; + len = len_; + } + inline const Type operator [] (unsigned int i) const + { + if (unlikely (i >= len)) return Type (); + return head[i]; + } + + inline void advance (unsigned int count) + { + if (unlikely (count > len)) + count = len; + len -= count; + head += count; + } + + private: + inline Supplier (const Supplier &); /* Disallow copy */ + inline Supplier& operator= (const Supplier &); /* Disallow copy */ + + unsigned int len; + const Type *head; +}; + + + + +/* + * + * The OpenType Font File: Data Types + */ + + +/* "The following data types are used in the OpenType font file. + * All OpenType fonts use Motorola-style byte ordering (Big Endian):" */ + +/* + * Int types + */ + + +template struct BEInt; + +template +struct BEInt +{ + public: + inline void set (Type V) + { + v = V; + } + inline operator Type (void) const + { + return v; + } + private: uint8_t v; +}; +template +struct BEInt +{ + public: + inline void set (Type V) + { + v[0] = (V >> 8) & 0xFF; + v[1] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 8) + + (v[1] ); + } + private: uint8_t v[2]; +}; +template +struct BEInt +{ + public: + inline void set (Type V) + { + v[0] = (V >> 16) & 0xFF; + v[1] = (V >> 8) & 0xFF; + v[2] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 16) + + (v[1] << 8) + + (v[2] ); + } + private: uint8_t v[3]; +}; +template +struct BEInt +{ + public: + inline void set (Type V) + { + v[0] = (V >> 24) & 0xFF; + v[1] = (V >> 16) & 0xFF; + v[2] = (V >> 8) & 0xFF; + v[3] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 24) + + (v[1] << 16) + + (v[2] << 8) + + (v[3] ); + } + private: uint8_t v[4]; +}; + +/* Integer types in big-endian order and no alignment requirement */ +template +struct IntType +{ + inline void set (Type i) { v.set (i); } + inline operator Type(void) const { return v; } + inline bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } + inline bool operator != (const IntType &o) const { return !(*this == o); } + static inline int cmp (const IntType *a, const IntType *b) { return b->cmp (*a); } + inline int cmp (Type a) const + { + Type b = v; + if (sizeof (Type) < sizeof (int)) + return (int) a - (int) b; + else + return a < b ? -1 : a == b ? 0 : +1; + } + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + protected: + BEInt v; + public: + DEFINE_SIZE_STATIC (Size); +}; + +typedef IntType BYTE; /* 8-bit unsigned integer. */ +typedef IntType USHORT; /* 16-bit unsigned integer. */ +typedef IntType SHORT; /* 16-bit signed integer. */ +typedef IntType ULONG; /* 32-bit unsigned integer. */ +typedef IntType LONG; /* 32-bit signed integer. */ +typedef IntType UINT24; /* 24-bit unsigned integer. */ + +/* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */ +typedef SHORT FWORD; + +/* 16-bit unsigned integer (USHORT) that describes a quantity in FUnits. */ +typedef USHORT UFWORD; + +/* Date represented in number of seconds since 12:00 midnight, January 1, + * 1904. The value is represented as a signed 64-bit integer. */ +struct LONGDATETIME +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + protected: + LONG major; + ULONG minor; + public: + DEFINE_SIZE_STATIC (8); +}; + +/* Array of four uint8s (length = 32 bits) used to identify a script, language + * system, feature, or baseline */ +struct Tag : ULONG +{ + /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ + inline operator const char* (void) const { return reinterpret_cast (&this->v); } + inline operator char* (void) { return reinterpret_cast (&this->v); } + public: + DEFINE_SIZE_STATIC (4); +}; +DEFINE_NULL_DATA (Tag, " "); + +/* Glyph index number, same as uint16 (length = 16 bits) */ +struct GlyphID : USHORT { + static inline int cmp (const GlyphID *a, const GlyphID *b) { return b->USHORT::cmp (*a); } + inline int cmp (hb_codepoint_t a) const { return (int) a - (int) *this; } +}; + +/* Script/language-system/feature index */ +struct Index : USHORT { + static const unsigned int NOT_FOUND_INDEX = 0xFFFFu; +}; +DEFINE_NULL_DATA (Index, "\xff\xff"); + +/* Offset, Null offset = 0 */ +template +struct Offset : Type +{ + inline bool is_null (void) const { return 0 == *this; } + public: + DEFINE_SIZE_STATIC (sizeof(Type)); +}; + + +/* CheckSum */ +struct CheckSum : ULONG +{ + /* This is reference implementation from the spec. */ + static inline uint32_t CalcTableChecksum (const ULONG *Table, uint32_t Length) + { + uint32_t Sum = 0L; + const ULONG *EndPtr = Table+((Length+3) & ~3) / ULONG::static_size; + + while (Table < EndPtr) + Sum += *Table++; + return Sum; + } + + /* Note: data should be 4byte aligned and have 4byte padding at the end. */ + inline void set_for_data (const void *data, unsigned int length) + { set (CalcTableChecksum ((const ULONG *) data, length)); } + + public: + DEFINE_SIZE_STATIC (4); +}; + + +/* + * Version Numbers + */ + +struct FixedVersion +{ + inline uint32_t to_int (void) const { return (major << 16) + minor; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + USHORT major; + USHORT minor; + public: + DEFINE_SIZE_STATIC (4); +}; + + + +/* + * Template subclasses of Offset that do the dereferencing. + * Use: (base+offset) + */ + +template +struct OffsetTo : Offset +{ + inline const Type& operator () (const void *base) const + { + unsigned int offset = *this; + if (unlikely (!offset)) return Null(Type); + return StructAtOffset (base, offset); + } + + inline Type& serialize (hb_serialize_context_t *c, const void *base) + { + Type *t = c->start_embed (); + this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ + return *t; + } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + unsigned int offset = *this; + if (unlikely (!offset)) return_trace (true); + const Type &obj = StructAtOffset (base, offset); + return_trace (likely (obj.sanitize (c)) || neuter (c)); + } + template + inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + unsigned int offset = *this; + if (unlikely (!offset)) return_trace (true); + const Type &obj = StructAtOffset (base, offset); + return_trace (likely (obj.sanitize (c, user_data)) || neuter (c)); + } + + /* Set the offset to Null */ + inline bool neuter (hb_sanitize_context_t *c) const { + return c->try_set (this, 0); + } + DEFINE_SIZE_STATIC (sizeof(OffsetType)); +}; +template +static inline const Type& operator + (const Base &base, const OffsetTo &offset) { return offset (base); } +template +static inline Type& operator + (Base &base, OffsetTo &offset) { return offset (base); } + + +/* + * Array Types + */ + +/* An array with a number of elements. */ +template +struct ArrayOf +{ + const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const + { + unsigned int count = len; + if (unlikely (start_offset > count)) + count = 0; + else + count -= start_offset; + count = MIN (count, *pcount); + *pcount = count; + return array + start_offset; + } + + inline const Type& operator [] (unsigned int i) const + { + if (unlikely (i >= len)) return Null(Type); + return array[i]; + } + inline Type& operator [] (unsigned int i) + { + return array[i]; + } + inline unsigned int get_size (void) const + { return len.static_size + len * Type::static_size; } + + inline bool serialize (hb_serialize_context_t *c, + unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + len.set (items_len); /* TODO(serialize) Overflow? */ + if (unlikely (!c->extend (*this))) return_trace (false); + return_trace (true); + } + + inline bool serialize (hb_serialize_context_t *c, + Supplier &items, + unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!serialize (c, items_len))) return_trace (false); + for (unsigned int i = 0; i < items_len; i++) + array[i] = items[i]; + items.advance (items_len); + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + + /* Note: for structs that do not reference other structs, + * we do not need to call their sanitize() as we already did + * a bound check on the aggregate array size. We just include + * a small unreachable expression to make sure the structs + * pointed to do have a simple sanitize(), ie. they do not + * reference other structs via offsets. + */ + (void) (false && array[0].sanitize (c)); + + return_trace (true); + } + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!array[i].sanitize (c, base))) + return_trace (false); + return_trace (true); + } + template + inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!array[i].sanitize (c, base, user_data))) + return_trace (false); + return_trace (true); + } + + template + inline int lsearch (const SearchType &x) const + { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (!this->array[i].cmp (x)) + return i; + return -1; + } + + private: + inline bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && c->check_array (array, Type::static_size, len)); + } + + public: + LenType len; + Type array[VAR]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), array); +}; + +/* Array of Offset's */ +template +struct OffsetArrayOf : ArrayOf > {}; + +/* Array of offsets relative to the beginning of the array itself. */ +template +struct OffsetListOf : OffsetArrayOf +{ + inline const Type& operator [] (unsigned int i) const + { + if (unlikely (i >= this->len)) return Null(Type); + return this+this->array[i]; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (OffsetArrayOf::sanitize (c, this)); + } + template + inline bool sanitize (hb_sanitize_context_t *c, T user_data) const + { + TRACE_SANITIZE (this); + return_trace (OffsetArrayOf::sanitize (c, this, user_data)); + } +}; + + +/* An array starting at second element. */ +template +struct HeadlessArrayOf +{ + inline const Type& operator [] (unsigned int i) const + { + if (unlikely (i >= len || !i)) return Null(Type); + return array[i-1]; + } + inline unsigned int get_size (void) const + { return len.static_size + (len ? len - 1 : 0) * Type::static_size; } + + inline bool serialize (hb_serialize_context_t *c, + Supplier &items, + unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + len.set (items_len); /* TODO(serialize) Overflow? */ + if (unlikely (!items_len)) return_trace (true); + if (unlikely (!c->extend (*this))) return_trace (false); + for (unsigned int i = 0; i < items_len - 1; i++) + array[i] = items[i]; + items.advance (items_len - 1); + return_trace (true); + } + + inline bool sanitize_shallow (hb_sanitize_context_t *c) const + { + return c->check_struct (this) + && c->check_array (this, Type::static_size, len); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + + /* Note: for structs that do not reference other structs, + * we do not need to call their sanitize() as we already did + * a bound check on the aggregate array size. We just include + * a small unreachable expression to make sure the structs + * pointed to do have a simple sanitize(), ie. they do not + * reference other structs via offsets. + */ + (void) (false && array[0].sanitize (c)); + + return_trace (true); + } + + LenType len; + Type array[VAR]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), array); +}; + + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedArrayOf : ArrayOf +{ + template + inline int bsearch (const SearchType &x) const + { + /* Hand-coded bsearch here since this is in the hot inner loop. */ + int min = 0, max = (int) this->len - 1; + while (min <= max) + { + int mid = (min + max) / 2; + int c = this->array[mid].cmp (x); + if (c < 0) + max = mid - 1; + else if (c > 0) + min = mid + 1; + else + return mid; + } + return -1; + } +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_TYPE_PRIVATE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-cmap-table.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-cmap-table.hh new file mode 100644 index 00000000000..b0e2f36a436 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-cmap-table.hh @@ -0,0 +1,528 @@ +/* + * Copyright © 2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_CMAP_TABLE_HH +#define HB_OT_CMAP_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * cmap -- Character To Glyph Index Mapping Table + */ + +#define HB_OT_TAG_cmap HB_TAG('c','m','a','p') + + +struct CmapSubtableFormat0 +{ + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; + if (!gid) + return false; + *glyph = gid; + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + USHORT format; /* Format number is set to 0. */ + USHORT lengthZ; /* Byte length of this subtable. */ + USHORT languageZ; /* Ignore. */ + BYTE glyphIdArray[256];/* An array that maps character + * code to glyph index values. */ + public: + DEFINE_SIZE_STATIC (6 + 256); +}; + +struct CmapSubtableFormat4 +{ + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + unsigned int segCount; + const USHORT *endCount; + const USHORT *startCount; + const USHORT *idDelta; + const USHORT *idRangeOffset; + const USHORT *glyphIdArray; + unsigned int glyphIdArrayLength; + + segCount = this->segCountX2 / 2; + endCount = this->values; + startCount = endCount + segCount + 1; + idDelta = startCount + segCount; + idRangeOffset = idDelta + segCount; + glyphIdArray = idRangeOffset + segCount; + glyphIdArrayLength = (this->length - 16 - 8 * segCount) / 2; + + /* Custom two-array bsearch. */ + int min = 0, max = (int) segCount - 1; + unsigned int i; + while (min <= max) + { + int mid = (min + max) / 2; + if (codepoint < startCount[mid]) + max = mid - 1; + else if (codepoint > endCount[mid]) + min = mid + 1; + else + { + i = mid; + goto found; + } + } + return false; + + found: + hb_codepoint_t gid; + unsigned int rangeOffset = idRangeOffset[i]; + if (rangeOffset == 0) + gid = codepoint + idDelta[i]; + else + { + /* Somebody has been smoking... */ + unsigned int index = rangeOffset / 2 + (codepoint - startCount[i]) + i - segCount; + if (unlikely (index >= glyphIdArrayLength)) + return false; + gid = glyphIdArray[index]; + if (unlikely (!gid)) + return false; + gid += idDelta[i]; + } + + *glyph = gid & 0xFFFFu; + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + if (unlikely (!c->check_range (this, length))) + { + /* Some broken fonts have too long of a "length" value. + * If that is the case, just change the value to truncate + * the subtable at the end of the blob. */ + uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); + if (!c->try_set (&length, new_length)) + return_trace (false); + } + + return_trace (16 + 4 * (unsigned int) segCountX2 <= length); + } + + protected: + USHORT format; /* Format number is set to 4. */ + USHORT length; /* This is the length in bytes of the + * subtable. */ + USHORT languageZ; /* Ignore. */ + USHORT segCountX2; /* 2 x segCount. */ + USHORT searchRangeZ; /* 2 * (2**floor(log2(segCount))) */ + USHORT entrySelectorZ; /* log2(searchRange/2) */ + USHORT rangeShiftZ; /* 2 x segCount - searchRange */ + + USHORT values[VAR]; +#if 0 + USHORT endCount[segCount]; /* End characterCode for each segment, + * last=0xFFFFu. */ + USHORT reservedPad; /* Set to 0. */ + USHORT startCount[segCount]; /* Start character code for each segment. */ + SHORT idDelta[segCount]; /* Delta for all character codes in segment. */ + USHORT idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ + USHORT glyphIdArray[VAR]; /* Glyph index array (arbitrary length) */ +#endif + + public: + DEFINE_SIZE_ARRAY (14, values); +}; + +struct CmapSubtableLongGroup +{ + friend struct CmapSubtableFormat12; + friend struct CmapSubtableFormat13; + + int cmp (hb_codepoint_t codepoint) const + { + if (codepoint < startCharCode) return -1; + if (codepoint > endCharCode) return +1; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + private: + ULONG startCharCode; /* First character code in this group. */ + ULONG endCharCode; /* Last character code in this group. */ + ULONG glyphID; /* Glyph index; interpretation depends on + * subtable format. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +template +struct CmapSubtableTrimmed +{ + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + /* Rely on our implicit array bound-checking. */ + hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode]; + if (!gid) + return false; + *glyph = gid; + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); + } + + protected: + UINT formatReserved; /* Subtable format and (maybe) padding. */ + UINT lengthZ; /* Byte length of this subtable. */ + UINT languageZ; /* Ignore. */ + UINT startCharCode; /* First character code covered. */ + ArrayOf + glyphIdArray; /* Array of glyph index values for character + * codes in the range. */ + public: + DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); +}; + +struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; + +template +struct CmapSubtableLongSegmented +{ + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + int i = groups.bsearch (codepoint); + if (i == -1) + return false; + *glyph = T::group_get_glyph (groups[i], codepoint); + return true; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && groups.sanitize (c)); + } + + protected: + USHORT format; /* Subtable format; set to 12. */ + USHORT reservedZ; /* Reserved; set to 0. */ + ULONG lengthZ; /* Byte length of this subtable. */ + ULONG languageZ; /* Ignore. */ + SortedArrayOf + groups; /* Groupings. */ + public: + DEFINE_SIZE_ARRAY (16, groups); +}; + +struct CmapSubtableFormat12 : CmapSubtableLongSegmented +{ + static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u) + { return group.glyphID + (u - group.startCharCode); } +}; + +struct CmapSubtableFormat13 : CmapSubtableLongSegmented +{ + static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u HB_UNUSED) + { return group.glyphID; } +}; + +typedef enum +{ + GLYPH_VARIANT_NOT_FOUND = 0, + GLYPH_VARIANT_FOUND = 1, + GLYPH_VARIANT_USE_DEFAULT = 2 +} glyph_variant_t; + +struct UnicodeValueRange +{ + inline int cmp (const hb_codepoint_t &codepoint) const + { + if (codepoint < startUnicodeValue) return -1; + if (codepoint > startUnicodeValue + additionalCount) return +1; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + UINT24 startUnicodeValue; /* First value in this range. */ + BYTE additionalCount; /* Number of additional values in this + * range. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +typedef SortedArrayOf DefaultUVS; + +struct UVSMapping +{ + inline int cmp (const hb_codepoint_t &codepoint) const + { + return unicodeValue.cmp (codepoint); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + UINT24 unicodeValue; /* Base Unicode value of the UVS */ + GlyphID glyphID; /* Glyph ID of the UVS */ + public: + DEFINE_SIZE_STATIC (5); +}; + +typedef SortedArrayOf NonDefaultUVS; + +struct VariationSelectorRecord +{ + inline glyph_variant_t get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph, + const void *base) const + { + int i; + const DefaultUVS &defaults = base+defaultUVS; + i = defaults.bsearch (codepoint); + if (i != -1) + return GLYPH_VARIANT_USE_DEFAULT; + const NonDefaultUVS &nonDefaults = base+nonDefaultUVS; + i = nonDefaults.bsearch (codepoint); + if (i != -1) + { + *glyph = nonDefaults[i].glyphID; + return GLYPH_VARIANT_FOUND; + } + return GLYPH_VARIANT_NOT_FOUND; + } + + inline int cmp (const hb_codepoint_t &variation_selector) const + { + return varSelector.cmp (variation_selector); + } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + defaultUVS.sanitize (c, base) && + nonDefaultUVS.sanitize (c, base)); + } + + UINT24 varSelector; /* Variation selector. */ + OffsetTo + defaultUVS; /* Offset to Default UVS Table. May be 0. */ + OffsetTo + nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ + public: + DEFINE_SIZE_STATIC (11); +}; + +struct CmapSubtableFormat14 +{ + inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + record.sanitize (c, this)); + } + + protected: + USHORT format; /* Format number is set to 0. */ + ULONG lengthZ; /* Byte length of this subtable. */ + SortedArrayOf + record; /* Variation selector records; sorted + * in increasing order of `varSelector'. */ + public: + DEFINE_SIZE_ARRAY (10, record); +}; + +struct CmapSubtable +{ + /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ + + inline bool get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph) const + { + switch (u.format) { + case 0: return u.format0 .get_glyph(codepoint, glyph); + case 4: return u.format4 .get_glyph(codepoint, glyph); + case 6: return u.format6 .get_glyph(codepoint, glyph); + case 10: return u.format10.get_glyph(codepoint, glyph); + case 12: return u.format12.get_glyph(codepoint, glyph); + case 13: return u.format13.get_glyph(codepoint, glyph); + case 14: + default: return false; + } + } + + inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + switch (u.format) { + case 14: return u.format14.get_glyph_variant(codepoint, variation_selector, glyph); + default: return GLYPH_VARIANT_NOT_FOUND; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0 .sanitize (c)); + case 4: return_trace (u.format4 .sanitize (c)); + case 6: return_trace (u.format6 .sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + case 12: return_trace (u.format12.sanitize (c)); + case 13: return_trace (u.format13.sanitize (c)); + case 14: return_trace (u.format14.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + USHORT format; /* Format identifier */ + CmapSubtableFormat0 format0; + CmapSubtableFormat4 format4; + CmapSubtableFormat6 format6; + CmapSubtableFormat10 format10; + CmapSubtableFormat12 format12; + CmapSubtableFormat13 format13; + CmapSubtableFormat14 format14; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +struct EncodingRecord +{ + inline int cmp (const EncodingRecord &other) const + { + int ret; + ret = platformID.cmp (other.platformID); + if (ret) return ret; + ret = encodingID.cmp (other.encodingID); + if (ret) return ret; + return 0; + } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + subtable.sanitize (c, base)); + } + + USHORT platformID; /* Platform ID. */ + USHORT encodingID; /* Platform-specific encoding ID. */ + OffsetTo + subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct cmap +{ + static const hb_tag_t tableTag = HB_OT_TAG_cmap; + + inline const CmapSubtable *find_subtable (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID.set (platform_id); + key.encodingID.set (encoding_id); + + /* Note: We can use bsearch, but since it has no performance + * implications, we use lsearch and as such accept fonts with + * unsorted subtable list. */ + int result = encodingRecord./*bsearch*/lsearch (key); + if (result == -1 || !encodingRecord[result].subtable) + return NULL; + + return &(this+encodingRecord[result].subtable); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version == 0) && + encodingRecord.sanitize (c, this)); + } + + USHORT version; /* Table version number (0). */ + SortedArrayOf + encodingRecord; /* Encoding tables. */ + public: + DEFINE_SIZE_ARRAY (4, encodingRecord); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_CMAP_TABLE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-font.cc b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-font.cc new file mode 100644 index 00000000000..836c3bf0e85 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-font.cc @@ -0,0 +1,434 @@ +/* + * Copyright © 2011,2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader + */ + +#include "hb-private.hh" + +#include "hb-ot.h" + +#include "hb-font-private.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-hmtx-table.hh" + + +struct hb_ot_face_metrics_accelerator_t +{ + unsigned int num_metrics; + unsigned int num_advances; + unsigned int default_advance; + const OT::_mtx *table; + hb_blob_t *blob; + + inline void init (hb_face_t *face, + hb_tag_t _hea_tag, hb_tag_t _mtx_tag, + unsigned int default_advance_) + { + this->default_advance = default_advance_; + this->num_metrics = face->get_num_glyphs (); + + hb_blob_t *_hea_blob = OT::Sanitizer::sanitize (face->reference_table (_hea_tag)); + const OT::_hea *_hea = OT::Sanitizer::lock_instance (_hea_blob); + this->num_advances = _hea->numberOfLongMetrics; + hb_blob_destroy (_hea_blob); + + this->blob = OT::Sanitizer::sanitize (face->reference_table (_mtx_tag)); + if (unlikely (!this->num_advances || + 2 * (this->num_advances + this->num_metrics) > hb_blob_get_length (this->blob))) + { + this->num_metrics = this->num_advances = 0; + hb_blob_destroy (this->blob); + this->blob = hb_blob_get_empty (); + } + this->table = OT::Sanitizer::lock_instance (this->blob); + } + + inline void fini (void) + { + hb_blob_destroy (this->blob); + } + + inline unsigned int get_advance (hb_codepoint_t glyph) const + { + if (unlikely (glyph >= this->num_metrics)) + { + /* If this->num_metrics is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, it means that the + * glyph index is out of bound: return zero. */ + if (this->num_metrics) + return 0; + else + return this->default_advance; + } + + if (glyph >= this->num_advances) + glyph = this->num_advances - 1; + + return this->table->longMetric[glyph].advance; + } +}; + +struct hb_ot_face_glyf_accelerator_t +{ + bool short_offset; + unsigned int num_glyphs; + const OT::loca *loca; + const OT::glyf *glyf; + hb_blob_t *loca_blob; + hb_blob_t *glyf_blob; + unsigned int glyf_len; + + inline void init (hb_face_t *face) + { + hb_blob_t *head_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_head)); + const OT::head *head = OT::Sanitizer::lock_instance (head_blob); + if ((unsigned int) head->indexToLocFormat > 1 || head->glyphDataFormat != 0) + { + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + hb_blob_destroy (head_blob); + return; + } + this->short_offset = 0 == head->indexToLocFormat; + hb_blob_destroy (head_blob); + + this->loca_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_loca)); + this->loca = OT::Sanitizer::lock_instance (this->loca_blob); + this->glyf_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_glyf)); + this->glyf = OT::Sanitizer::lock_instance (this->glyf_blob); + + this->num_glyphs = MAX (1u, hb_blob_get_length (this->loca_blob) / (this->short_offset ? 2 : 4)) - 1; + this->glyf_len = hb_blob_get_length (this->glyf_blob); + } + + inline void fini (void) + { + hb_blob_destroy (this->loca_blob); + hb_blob_destroy (this->glyf_blob); + } + + inline bool get_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + if (unlikely (glyph >= this->num_glyphs)) + return false; + + unsigned int start_offset, end_offset; + if (this->short_offset) + { + start_offset = 2 * this->loca->u.shortsZ[glyph]; + end_offset = 2 * this->loca->u.shortsZ[glyph + 1]; + } + else + { + start_offset = this->loca->u.longsZ[glyph]; + end_offset = this->loca->u.longsZ[glyph + 1]; + } + + if (start_offset > end_offset || end_offset > this->glyf_len) + return false; + + if (end_offset - start_offset < OT::glyfGlyphHeader::static_size) + return true; /* Empty glyph; zero extents. */ + + const OT::glyfGlyphHeader &glyph_header = OT::StructAtOffset (this->glyf, start_offset); + + extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); + extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); + extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; + extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; + + return true; + } +}; + +struct hb_ot_face_cmap_accelerator_t +{ + const OT::CmapSubtable *table; + const OT::CmapSubtable *uvs_table; + hb_blob_t *blob; + + inline void init (hb_face_t *face) + { + this->blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_cmap)); + const OT::cmap *cmap = OT::Sanitizer::lock_instance (this->blob); + const OT::CmapSubtable *subtable = NULL; + const OT::CmapSubtable *subtable_uvs = NULL; + + /* 32-bit subtables. */ + if (!subtable) subtable = cmap->find_subtable (3, 10); + if (!subtable) subtable = cmap->find_subtable (0, 6); + if (!subtable) subtable = cmap->find_subtable (0, 4); + /* 16-bit subtables. */ + if (!subtable) subtable = cmap->find_subtable (3, 1); + if (!subtable) subtable = cmap->find_subtable (0, 3); + if (!subtable) subtable = cmap->find_subtable (0, 2); + if (!subtable) subtable = cmap->find_subtable (0, 1); + if (!subtable) subtable = cmap->find_subtable (0, 0); + if (!subtable) subtable = cmap->find_subtable (3, 0); + /* Meh. */ + if (!subtable) subtable = &OT::Null(OT::CmapSubtable); + + /* UVS subtable. */ + if (!subtable_uvs) subtable_uvs = cmap->find_subtable (0, 5); + /* Meh. */ + if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtable); + + this->table = subtable; + this->uvs_table = subtable_uvs; + } + + inline void fini (void) + { + hb_blob_destroy (this->blob); + } + + inline bool get_glyph (hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + if (unlikely (variation_selector)) + { + switch (this->uvs_table->get_glyph_variant (unicode, + variation_selector, + glyph)) + { + case OT::GLYPH_VARIANT_NOT_FOUND: return false; + case OT::GLYPH_VARIANT_FOUND: return true; + case OT::GLYPH_VARIANT_USE_DEFAULT: break; + } + } + + return this->table->get_glyph (unicode, glyph); + } +}; + + +struct hb_ot_font_t +{ + hb_ot_face_cmap_accelerator_t cmap; + hb_ot_face_metrics_accelerator_t h_metrics; + hb_ot_face_metrics_accelerator_t v_metrics; + hb_ot_face_glyf_accelerator_t glyf; +}; + + +static hb_ot_font_t * +_hb_ot_font_create (hb_face_t *face) +{ + hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t)); + + if (unlikely (!ot_font)) + return NULL; + + unsigned int upem = face->get_upem (); + + ot_font->cmap.init (face); + ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, upem>>1); + ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, upem); /* TODO Can we do this lazily? */ + ot_font->glyf.init (face); + + return ot_font; +} + +static void +_hb_ot_font_destroy (hb_ot_font_t *ot_font) +{ + ot_font->cmap.fini (); + ot_font->h_metrics.fini (); + ot_font->v_metrics.fini (); + ot_font->glyf.fini (); + + free (ot_font); +} + + +static hb_bool_t +hb_ot_get_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) + +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + return ot_font->cmap.get_glyph (unicode, variation_selector, glyph); +} + +static hb_position_t +hb_ot_get_glyph_h_advance (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + return font->em_scale_x (ot_font->h_metrics.get_advance (glyph)); +} + +static hb_position_t +hb_ot_get_glyph_v_advance (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph)); +} + +static hb_bool_t +hb_ot_get_glyph_h_origin (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x HB_UNUSED, + hb_position_t *y HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* We always work in the horizontal coordinates. */ + return true; +} + +static hb_bool_t +hb_ot_get_glyph_v_origin (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + /* TODO */ + return false; +} + +static hb_position_t +hb_ot_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + /* TODO */ + return 0; +} + +static hb_position_t +hb_ot_get_glyph_v_kerning (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph HB_UNUSED, + hb_codepoint_t bottom_glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* OpenType doesn't have vertical-kerning other than GPOS. */ + return 0; +} + +static hb_bool_t +hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + bool ret = ot_font->glyf.get_extents (glyph, extents); + extents->x_bearing = font->em_scale_x (extents->x_bearing); + extents->y_bearing = font->em_scale_y (extents->y_bearing); + extents->width = font->em_scale_x (extents->width); + extents->height = font->em_scale_y (extents->height); + return ret; +} + +static hb_bool_t +hb_ot_get_glyph_contour_point (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + /* TODO */ + return false; +} + +static hb_bool_t +hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + /* TODO */ + return false; +} + +static hb_bool_t +hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + /* TODO */ + return false; +} + + +static hb_font_funcs_t * +_hb_ot_get_font_funcs (void) +{ + static const hb_font_funcs_t ot_ffuncs = { + HB_OBJECT_HEADER_STATIC, + + true, /* immutable */ + + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_ot_get_##name, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + }; + + return const_cast (&ot_ffuncs); +} + + +/** + * Since: 0.9.28 + **/ +void +hb_ot_font_set_funcs (hb_font_t *font) +{ + hb_ot_font_t *ot_font = _hb_ot_font_create (font->face); + if (unlikely (!ot_font)) + return; + + hb_font_set_funcs (font, + _hb_ot_get_font_funcs (), + ot_font, + (hb_destroy_func_t) _hb_ot_font_destroy); +} diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-font.h b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-font.h new file mode 100644 index 00000000000..b9947a16bc8 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-font.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_FONT_H +#define HB_OT_FONT_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +void +hb_ot_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_OT_FONT_H */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-glyf-table.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-glyf-table.hh new file mode 100644 index 00000000000..616bd6650d8 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-glyf-table.hh @@ -0,0 +1,104 @@ +/* + * Copyright © 2015 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_GLYF_TABLE_HH +#define HB_OT_GLYF_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * loca -- Index to Location + */ + +#define HB_OT_TAG_loca HB_TAG('l','o','c','a') + + +struct loca +{ + static const hb_tag_t tableTag = HB_OT_TAG_loca; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (true); + } + + public: + union { + USHORT shortsZ[VAR]; /* Location offset divided by 2. */ + ULONG longsZ[VAR]; /* Location offset. */ + } u; + DEFINE_SIZE_ARRAY (0, u.longsZ); +}; + + +/* + * glyf -- TrueType Glyph Data + */ + +#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') + + +struct glyf +{ + static const hb_tag_t tableTag = HB_OT_TAG_glyf; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* We don't check for anything specific here. The users of the + * struct do all the hard work... */ + return_trace (true); + } + + public: + BYTE dataX[VAR]; /* Glyphs data. */ + + DEFINE_SIZE_ARRAY (0, dataX); +}; + +struct glyfGlyphHeader +{ + SHORT numberOfContours; /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + SHORT xMin; /* Minimum x for coordinate data. */ + SHORT yMin; /* Minimum y for coordinate data. */ + SHORT xMax; /* Maximum x for coordinate data. */ + SHORT yMax; /* Maximum y for coordinate data. */ + + DEFINE_SIZE_STATIC (10); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_GLYF_TABLE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-head-table.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-head-table.hh new file mode 100644 index 00000000000..3cd59fbf2dd --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-head-table.hh @@ -0,0 +1,152 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HEAD_TABLE_HH +#define HB_OT_HEAD_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * head -- Font Header + */ + +#define HB_OT_TAG_head HB_TAG('h','e','a','d') + +struct head +{ + static const hb_tag_t tableTag = HB_OT_TAG_head; + + inline unsigned int get_upem (void) const + { + unsigned int upem = unitsPerEm; + /* If no valid head table found, assume 1000, which matches typical Type1 usage. */ + return 16 <= upem && upem <= 16384 ? upem : 1000; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && likely (version.major == 1)); + } + + protected: + FixedVersion version; /* Version of the head table--currently + * 0x00010000u for version 1.0. */ + FixedVersion fontRevision; /* Set by font manufacturer. */ + ULONG checkSumAdjustment; /* To compute: set it to 0, sum the + * entire font as ULONG, then store + * 0xB1B0AFBAu - sum. */ + ULONG magicNumber; /* Set to 0x5F0F3CF5u. */ + USHORT flags; /* Bit 0: Baseline for font at y=0; + * Bit 1: Left sidebearing point at x=0; + * Bit 2: Instructions may depend on point size; + * Bit 3: Force ppem to integer values for all + * internal scaler math; may use fractional + * ppem sizes if this bit is clear; + * Bit 4: Instructions may alter advance width + * (the advance widths might not scale linearly); + + * Bits 5-10: These should be set according to + * Apple's specification. However, they are not + * implemented in OpenType. + * Bit 5: This bit should be set in fonts that are + * intended to e laid out vertically, and in + * which the glyphs have been drawn such that an + * x-coordinate of 0 corresponds to the desired + * vertical baseline. + * Bit 6: This bit must be set to zero. + * Bit 7: This bit should be set if the font + * requires layout for correct linguistic + * rendering (e.g. Arabic fonts). + * Bit 8: This bit should be set for a GX font + * which has one or more metamorphosis effects + * designated as happening by default. + * Bit 9: This bit should be set if the font + * contains any strong right-to-left glyphs. + * Bit 10: This bit should be set if the font + * contains Indic-style rearrangement effects. + + * Bit 11: Font data is 'lossless,' as a result + * of having been compressed and decompressed + * with the Agfa MicroType Express engine. + * Bit 12: Font converted (produce compatible metrics) + * Bit 13: Font optimized for ClearType™. + * Note, fonts that rely on embedded bitmaps (EBDT) + * for rendering should not be considered optimized + * for ClearType, and therefore should keep this bit + * cleared. + * Bit 14: Last Resort font. If set, indicates that + * the glyphs encoded in the cmap subtables are simply + * generic symbolic representations of code point + * ranges and don’t truly represent support for those + * code points. If unset, indicates that the glyphs + * encoded in the cmap subtables represent proper + * support for those code points. + * Bit 15: Reserved, set to 0. */ + USHORT unitsPerEm; /* Valid range is from 16 to 16384. This value + * should be a power of 2 for fonts that have + * TrueType outlines. */ + LONGDATETIME created; /* Number of seconds since 12:00 midnight, + January 1, 1904. 64-bit integer */ + LONGDATETIME modified; /* Number of seconds since 12:00 midnight, + January 1, 1904. 64-bit integer */ + SHORT xMin; /* For all glyph bounding boxes. */ + SHORT yMin; /* For all glyph bounding boxes. */ + SHORT xMax; /* For all glyph bounding boxes. */ + SHORT yMax; /* For all glyph bounding boxes. */ + USHORT macStyle; /* Bit 0: Bold (if set to 1); + * Bit 1: Italic (if set to 1) + * Bit 2: Underline (if set to 1) + * Bit 3: Outline (if set to 1) + * Bit 4: Shadow (if set to 1) + * Bit 5: Condensed (if set to 1) + * Bit 6: Extended (if set to 1) + * Bits 7-15: Reserved (set to 0). */ + USHORT lowestRecPPEM; /* Smallest readable size in pixels. */ + SHORT fontDirectionHint; /* Deprecated (Set to 2). + * 0: Fully mixed directional glyphs; + * 1: Only strongly left to right; + * 2: Like 1 but also contains neutrals; + * -1: Only strongly right to left; + * -2: Like -1 but also contains neutrals. */ + public: + SHORT indexToLocFormat; /* 0 for short offsets, 1 for long. */ + SHORT glyphDataFormat; /* 0 for current format. */ + + DEFINE_SIZE_STATIC (54); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HEAD_TABLE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-hhea-table.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-hhea-table.hh new file mode 100644 index 00000000000..ed65934fae4 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-hhea-table.hh @@ -0,0 +1,103 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HHEA_TABLE_HH +#define HB_OT_HHEA_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * hhea -- The Horizontal Header Table + * vhea -- The Vertical Header Table + */ + +#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') +#define HB_OT_TAG_vhea HB_TAG('v','h','e','a') + + +struct _hea +{ + static const hb_tag_t tableTag = HB_TAG('_','h','e','a'); + + static const hb_tag_t hheaTag = HB_OT_TAG_hhea; + static const hb_tag_t vheaTag = HB_OT_TAG_vhea; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && likely (version.major == 1)); + } + + public: + FixedVersion version; /* 0x00010000u for version 1.0. */ + FWORD ascender; /* Typographic ascent. */ + FWORD descender; /* Typographic descent. */ + FWORD lineGap; /* Typographic line gap. */ + UFWORD advanceMax; /* Maximum advance width/height value in + * metrics table. */ + FWORD minLeadingBearing; /* Minimum left/top sidebearing value in + * metrics table. */ + FWORD minTrailingBearing; /* Minimum right/bottom sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)) for horizontal. */ + FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), + * vertical: minLeadingBearing+(yMax-yMin). */ + SHORT caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical caret, + * 0 for horizontal.*/ + SHORT caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ + SHORT caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non-slanted fonts. */ + SHORT reserved1; /* Set to 0. */ + SHORT reserved2; /* Set to 0. */ + SHORT reserved3; /* Set to 0. */ + SHORT reserved4; /* Set to 0. */ + SHORT metricDataFormat; /* 0 for current format. */ + USHORT numberOfLongMetrics; /* Number of LongMetric entries in metric + * table. */ + public: + DEFINE_SIZE_STATIC (36); +}; + +struct hhea : _hea { + static const hb_tag_t tableTag = HB_OT_TAG_hhea; +}; +struct vhea : _hea { + static const hb_tag_t tableTag = HB_OT_TAG_vhea; +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HHEA_TABLE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-hmtx-table.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-hmtx-table.hh new file mode 100644 index 00000000000..cd266ffbee7 --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-hmtx-table.hh @@ -0,0 +1,104 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HMTX_TABLE_HH +#define HB_OT_HMTX_TABLE_HH + +#include "hb-open-type-private.hh" + + +namespace OT { + + +/* + * hmtx -- The Horizontal Metrics Table + * vmtx -- The Vertical Metrics Table + */ + +#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') +#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') + + +struct LongMetric +{ + USHORT advance; /* Advance width/height. */ + SHORT lsb; /* Leading (left/top) side bearing. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct _mtx +{ + static const hb_tag_t tableTag = HB_TAG('_','m','t','x'); + + static const hb_tag_t hmtxTag = HB_OT_TAG_hmtx; + static const hb_tag_t vmtxTag = HB_OT_TAG_vmtx; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* We don't check for anything specific here. The users of the + * struct do all the hard work... */ + return_trace (true); + } + + public: + LongMetric longMetric[VAR]; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ + SHORT leadingBearingX[VAR]; /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ + public: + DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX); +}; + +struct hmtx : _mtx { + static const hb_tag_t tableTag = HB_OT_TAG_hmtx; +}; +struct vmtx : _mtx { + static const hb_tag_t tableTag = HB_OT_TAG_vmtx; +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HMTX_TABLE_HH */ diff --git a/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-common-private.hh b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-common-private.hh new file mode 100644 index 00000000000..f087b2317cb --- /dev/null +++ b/jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-layout-common-private.hh @@ -0,0 +1,1224 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH +#define HB_OT_LAYOUT_COMMON_PRIVATE_HH + +#include "hb-ot-layout-private.hh" +#include "hb-open-type-private.hh" +#include "hb-set-private.hh" + + +namespace OT { + + +#define TRACE_DISPATCH(this, format) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "format %d", (int) format); + + +#define NOT_COVERED ((unsigned int) -1) +#define MAX_NESTING_LEVEL 6 +#define MAX_CONTEXT_LENGTH 64 + + + +/* + * + * OpenType Layout Common Table Formats + * + */ + + +/* + * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList + */ + +template +struct Record +{ + inline int cmp (hb_tag_t a) const { + return tag.cmp (a); + } + + struct sanitize_closure_t { + hb_tag_t tag; + const void *list_base; + }; + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + const sanitize_closure_t closure = {tag, base}; + return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); + } + + Tag tag; /* 4-byte Tag identifier */ + OffsetTo + offset; /* Offset from beginning of object holding + * the Record */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct RecordArrayOf : SortedArrayOf > { + inline const Tag& get_tag (unsigned int i) const + { + /* We cheat slightly and don't define separate Null objects + * for Record types. Instead, we return the correct Null(Tag) + * here. */ + if (unlikely (i >= this->len)) return Null(Tag); + return (*this)[i].tag; + } + inline unsigned int get_tags (unsigned int start_offset, + unsigned int *record_count /* IN/OUT */, + hb_tag_t *record_tags /* OUT */) const + { + if (record_count) { + const Record *arr = this->sub_array (start_offset, record_count); + unsigned int count = *record_count; + for (unsigned int i = 0; i < count; i++) + record_tags[i] = arr[i].tag; + } + return this->len; + } + inline bool find_index (hb_tag_t tag, unsigned int *index) const + { + /* If we want to allow non-sorted data, we can lsearch(). */ + int i = this->/*lsearch*/bsearch (tag); + if (i != -1) { + if (index) *index = i; + return true; + } else { + if (index) *index = Index::NOT_FOUND_INDEX; + return false; + } + } +}; + +template +struct RecordListOf : RecordArrayOf +{ + inline const Type& operator [] (unsigned int i) const + { return this+RecordArrayOf::operator [](i).offset; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (RecordArrayOf::sanitize (c, this)); + } +}; + + +struct RangeRecord +{ + inline int cmp (hb_codepoint_t g) const { + return g < start ? -1 : g <= end ? 0 : +1 ; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline bool intersects (const hb_set_t *glyphs) const { + return glyphs->intersects (start, end); + } + + template + inline void add_coverage (set_t *glyphs) const { + glyphs->add_range (start, end); + } + + GlyphID start; /* First GlyphID in the range */ + GlyphID end; /* Last GlyphID in the range */ + USHORT value; /* Value */ + public: + DEFINE_SIZE_STATIC (6); +}; +DEFINE_NULL_DATA (RangeRecord, "\000\001"); + + +struct IndexArray : ArrayOf +{ + inline unsigned int get_indexes (unsigned int start_offset, + unsigned int *_count /* IN/OUT */, + unsigned int *_indexes /* OUT */) const + { + if (_count) { + const USHORT *arr = this->sub_array (start_offset, _count); + unsigned int count = *_count; + for (unsigned int i = 0; i < count; i++) + _indexes[i] = arr[i]; + } + return this->len; + } +}; + + +struct Script; +struct LangSys; +struct Feature; + + +struct LangSys +{ + inline unsigned int get_feature_count (void) const + { return featureIndex.len; } + inline hb_tag_t get_feature_index (unsigned int i) const + { return featureIndex[i]; } + inline unsigned int get_feature_indexes (unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + unsigned int *feature_indexes /* OUT */) const + { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } + + inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; } + inline unsigned int get_required_feature_index (void) const + { + if (reqFeatureIndex == 0xFFFFu) + return Index::NOT_FOUND_INDEX; + return reqFeatureIndex;; + } + + inline bool sanitize (hb_sanitize_context_t *c, + const Record::sanitize_closure_t * = NULL) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && featureIndex.sanitize (c)); + } + + Offset<> lookupOrderZ; /* = Null (reserved for an offset to a + * reordering table) */ + USHORT reqFeatureIndex;/* Index of a feature required for this + * language system--if no required features + * = 0xFFFFu */ + IndexArray featureIndex; /* Array of indices into the FeatureList */ + public: + DEFINE_SIZE_ARRAY (6, featureIndex); +}; +DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF"); + + +struct Script +{ + inline unsigned int get_lang_sys_count (void) const + { return langSys.len; } + inline const Tag& get_lang_sys_tag (unsigned int i) const + { return langSys.get_tag (i); } + inline unsigned int get_lang_sys_tags (unsigned int start_offset, + unsigned int *lang_sys_count /* IN/OUT */, + hb_tag_t *lang_sys_tags /* OUT */) const + { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } + inline const LangSys& get_lang_sys (unsigned int i) const + { + if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); + return this+langSys[i].offset; + } + inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const + { return langSys.find_index (tag, index); } + + inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; } + inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } + + inline bool sanitize (hb_sanitize_context_t *c, + const Record