Skip to content

Commit a3cdac4

Browse files
Move MacHelper.PListWrapper into PListReader and add unit tests
1 parent ca8f71c commit a3cdac4

File tree

7 files changed

+395
-116
lines changed

7 files changed

+395
-116
lines changed

src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,9 @@
2929
import java.io.IOException;
3030
import java.nio.file.Files;
3131
import java.nio.file.Path;
32-
33-
import javax.xml.xpath.XPath;
34-
import javax.xml.xpath.XPathConstants;
35-
import javax.xml.xpath.XPathExpressionException;
36-
import javax.xml.xpath.XPathFactory;
37-
38-
import org.w3c.dom.Document;
39-
import org.xml.sax.SAXException;
40-
4132
import jdk.jpackage.internal.model.DottedVersion;
33+
import jdk.jpackage.internal.util.PListReader;
34+
import org.xml.sax.SAXException;
4235

4336
/**
4437
* Mandatory elements of Info.plist file of app image.
@@ -56,30 +49,19 @@ static final class InvalidPlistFileException extends Exception {
5649

5750
static AppImageInfoPListFile loadFromInfoPList(Path infoPListFile)
5851
throws IOException, InvalidPlistFileException, SAXException {
59-
final var doc = initDocumentBuilder().parse(Files.newInputStream(infoPListFile));
6052

61-
XPath xPath = XPathFactory.newInstance().newXPath();
53+
final var plistReader = new PListReader(initDocumentBuilder().parse(Files.newInputStream(infoPListFile)));
6254

6355
try {
6456
return new AppImageInfoPListFile(
65-
getStringValue(doc, xPath, "CFBundleIdentifier"),
66-
getStringValue(doc, xPath, "CFBundleName"),
67-
getStringValue(doc, xPath, "NSHumanReadableCopyright"),
68-
DottedVersion.greedy(getStringValue(doc, xPath, "CFBundleShortVersionString")),
69-
DottedVersion.greedy(getStringValue(doc, xPath, "CFBundleVersion")),
70-
getStringValue(doc, xPath, "LSApplicationCategoryType"));
71-
} catch (XPathExpressionException ex) {
72-
// This should never happen as XPath expressions should be correct
73-
throw new RuntimeException(ex);
57+
plistReader.queryValue("CFBundleIdentifier"),
58+
plistReader.queryValue("CFBundleName"),
59+
plistReader.queryValue("NSHumanReadableCopyright"),
60+
DottedVersion.greedy(plistReader.queryValue("CFBundleShortVersionString")),
61+
DottedVersion.greedy(plistReader.queryValue("CFBundleVersion")),
62+
plistReader.queryValue("LSApplicationCategoryType"));
7463
} catch (Exception ex) {
7564
throw new InvalidPlistFileException(ex);
7665
}
7766
}
78-
79-
private static String getStringValue(Document doc, XPath xPath, String elementName) throws XPathExpressionException {
80-
// Query for the value of <string> element preceding <key>
81-
// element with value equal to the value of `elementName`
82-
return (String) xPath.evaluate(String.format("//string[preceding-sibling::key = \"%s\"][1]", elementName),
83-
doc, XPathConstants.STRING);
84-
}
8567
}

src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
*/
2525
package jdk.jpackage.internal;
2626

27-
import static jdk.jpackage.internal.util.PListUtils.writeArray;
28-
import static jdk.jpackage.internal.util.PListUtils.writeBoolean;
29-
import static jdk.jpackage.internal.util.PListUtils.writeDict;
30-
import static jdk.jpackage.internal.util.PListUtils.writeKey;
31-
import static jdk.jpackage.internal.util.PListUtils.writeString;
32-
import static jdk.jpackage.internal.util.PListUtils.writeStringArray;
27+
import static jdk.jpackage.internal.util.PListWriter.writeArray;
28+
import static jdk.jpackage.internal.util.PListWriter.writeBoolean;
29+
import static jdk.jpackage.internal.util.PListWriter.writeDict;
30+
import static jdk.jpackage.internal.util.PListWriter.writeKey;
31+
import static jdk.jpackage.internal.util.PListWriter.writeString;
32+
import static jdk.jpackage.internal.util.PListWriter.writeStringArray;
3333
import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer;
3434
import static jdk.jpackage.internal.util.function.ThrowingBiConsumer.toBiConsumer;
3535
import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier;

src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListUtils.java renamed to src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import javax.xml.stream.XMLStreamException;
3232
import javax.xml.stream.XMLStreamWriter;
3333

34-
public final class PListUtils {
34+
public final class PListWriter {
3535

3636
public static void writeBoolean(XMLStreamWriter xml, String key, boolean value)
3737
throws XMLStreamException {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.jpackage.internal.util;
26+
27+
import java.io.ByteArrayInputStream;
28+
import java.io.IOException;
29+
import java.util.List;
30+
import java.util.NoSuchElementException;
31+
import java.util.Objects;
32+
import java.util.Optional;
33+
import javax.xml.parsers.ParserConfigurationException;
34+
import javax.xml.xpath.XPathConstants;
35+
import javax.xml.xpath.XPathFactory;
36+
import jdk.jpackage.internal.util.function.ThrowingSupplier;
37+
import org.w3c.dom.Node;
38+
import org.xml.sax.SAXException;
39+
40+
public final class PListReader {
41+
42+
public String queryValue(String keyName) {
43+
final var node = getNode(keyName);
44+
switch (node.getNodeName()) {
45+
case "string" -> {
46+
return node.getTextContent();
47+
}
48+
default -> {
49+
throw new NoSuchElementException();
50+
}
51+
}
52+
}
53+
54+
public boolean queryBoolValue(String keyName) {
55+
final var node = getNode(keyName);
56+
switch (node.getNodeName()) {
57+
case "true" -> {
58+
return true;
59+
}
60+
case "false" -> {
61+
return false;
62+
}
63+
default -> {
64+
throw new NoSuchElementException();
65+
}
66+
}
67+
}
68+
69+
public List<String> queryArrayValue(String keyName) {
70+
final var node = getNode(keyName);
71+
switch (node.getNodeName()) {
72+
case "array" -> {
73+
return XmlUtils.toStream(node.getChildNodes()).filter(n -> {
74+
return n.getNodeName().equals("string");
75+
}).map(Node::getTextContent).toList();
76+
}
77+
default -> {
78+
throw new NoSuchElementException();
79+
}
80+
}
81+
}
82+
83+
public PListReader(Node doc) {
84+
this.root = Objects.requireNonNull(doc);
85+
}
86+
87+
public PListReader(byte[] xmlData) throws ParserConfigurationException, SAXException, IOException {
88+
this(XmlUtils.initDocumentBuilder().parse(new ByteArrayInputStream(xmlData)));
89+
}
90+
91+
private Node getNode(String keyName) {
92+
final var xPath = XPathFactory.newInstance().newXPath();
93+
final var query = String.format("//*[preceding-sibling::key = \"%s\"][1]", keyName);
94+
return Optional.ofNullable(ThrowingSupplier.toSupplier(() -> {
95+
return (Node) xPath.evaluate(query, root, XPathConstants.NODE);
96+
}).get()).orElseThrow(NoSuchElementException::new);
97+
}
98+
99+
private final Node root;
100+
}

test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java

Lines changed: 9 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,21 @@
3131
import java.nio.charset.StandardCharsets;
3232
import java.nio.file.Files;
3333
import java.nio.file.Path;
34-
import java.util.ArrayList;
3534
import java.util.List;
3635
import java.util.Optional;
3736
import java.util.Set;
3837
import java.util.regex.Pattern;
3938
import java.util.stream.Collectors;
4039
import java.util.stream.Stream;
41-
import javax.xml.parsers.DocumentBuilder;
42-
import javax.xml.parsers.DocumentBuilderFactory;
43-
import javax.xml.parsers.ParserConfigurationException;
44-
import javax.xml.xpath.XPath;
4540
import javax.xml.xpath.XPathConstants;
4641
import javax.xml.xpath.XPathFactory;
4742
import jdk.jpackage.internal.RetryExecutor;
43+
import jdk.jpackage.internal.util.PListReader;
4844
import jdk.jpackage.internal.util.PathUtils;
45+
import jdk.jpackage.internal.util.XmlUtils;
4946
import jdk.jpackage.internal.util.function.ThrowingConsumer;
5047
import jdk.jpackage.internal.util.function.ThrowingSupplier;
5148
import jdk.jpackage.test.PackageTest.PackageHandlers;
52-
import org.w3c.dom.NodeList;
53-
import org.xml.sax.SAXException;
5449

5550
public final class MacHelper {
5651

@@ -128,25 +123,25 @@ public static void withExplodedDmg(JPackageCommand cmd,
128123
}
129124
}
130125

131-
public static PListWrapper readPListFromAppImage(Path appImage) {
126+
public static PListReader readPListFromAppImage(Path appImage) {
132127
return readPList(appImage.resolve("Contents/Info.plist"));
133128
}
134129

135-
public static PListWrapper readPList(Path path) {
130+
public static PListReader readPList(Path path) {
136131
TKit.assertReadableFileExists(path);
137132
return ThrowingSupplier.toSupplier(() -> readPList(Files.readAllLines(
138133
path))).get();
139134
}
140135

141-
public static PListWrapper readPList(List<String> lines) {
136+
public static PListReader readPList(List<String> lines) {
142137
return readPList(lines.stream());
143138
}
144139

145-
public static PListWrapper readPList(Stream<String> lines) {
146-
return ThrowingSupplier.toSupplier(() -> new PListWrapper(lines
140+
public static PListReader readPList(Stream<String> lines) {
141+
return ThrowingSupplier.toSupplier(() -> new PListReader(lines
147142
// Skip leading lines before xml declaration
148143
.dropWhile(Pattern.compile("\\s?<\\?xml\\b.+\\?>").asPredicate().negate())
149-
.collect(Collectors.joining()))).get();
144+
.collect(Collectors.joining()).getBytes(StandardCharsets.UTF_8))).get();
150145
}
151146

152147
static PackageHandlers createDmgPackageHandlers() {
@@ -217,7 +212,7 @@ static PackageHandlers createPkgPackageHandlers() {
217212
}).forEach(ThrowingConsumer.toConsumer(pkgDir -> {
218213
// Installation root of the package is stored in
219214
// /pkg-info@install-location attribute in $pkgDir/PackageInfo xml file
220-
var doc = createDocumentBuilder().parse(
215+
var doc = XmlUtils.initDocumentBuilder().parse(
221216
new ByteArrayInputStream(Files.readAllBytes(
222217
pkgDir.resolve("PackageInfo"))));
223218
var xPath = XPathFactory.newInstance().newXPath();
@@ -341,69 +336,6 @@ private static String getPackageId(JPackageCommand cmd) {
341336
});
342337
}
343338

344-
public static final class PListWrapper {
345-
public String queryValue(String keyName) {
346-
XPath xPath = XPathFactory.newInstance().newXPath();
347-
// Query for the value of <string> element preceding <key> element
348-
// with value equal to `keyName`
349-
String query = String.format(
350-
"//string[preceding-sibling::key = \"%s\"][1]", keyName);
351-
return ThrowingSupplier.toSupplier(() -> (String) xPath.evaluate(
352-
query, doc, XPathConstants.STRING)).get();
353-
}
354-
355-
public Boolean queryBoolValue(String keyName) {
356-
XPath xPath = XPathFactory.newInstance().newXPath();
357-
// Query boolean element preceding <key> element
358-
// with value equal to `keyName`
359-
String query = String.format(
360-
"name(//*[preceding-sibling::key = \"%s\"])", keyName);
361-
String value = ThrowingSupplier.toSupplier(() -> (String) xPath.evaluate(
362-
query, doc, XPathConstants.STRING)).get();
363-
return Boolean.valueOf(value);
364-
}
365-
366-
public List<String> queryArrayValue(String keyName) {
367-
XPath xPath = XPathFactory.newInstance().newXPath();
368-
// Query string array preceding <key> element with value equal to `keyName`
369-
String query = String.format(
370-
"//array[preceding-sibling::key = \"%s\"]", keyName);
371-
NodeList list = ThrowingSupplier.toSupplier(() -> (NodeList) xPath.evaluate(
372-
query, doc, XPathConstants.NODESET)).get();
373-
if (list.getLength() != 1) {
374-
throw new RuntimeException(
375-
String.format("Unable to find <array> element for key = \"%s\"]",
376-
keyName));
377-
}
378-
379-
NodeList childList = list.item(0).getChildNodes();
380-
List<String> values = new ArrayList<>(childList.getLength());
381-
for (int i = 0; i < childList.getLength(); i++) {
382-
if (childList.item(i).getNodeName().equals("string")) {
383-
values.add(childList.item(i).getTextContent());
384-
}
385-
}
386-
return values;
387-
}
388-
389-
private PListWrapper(String xml) throws ParserConfigurationException,
390-
SAXException, IOException {
391-
doc = createDocumentBuilder().parse(new ByteArrayInputStream(
392-
xml.getBytes(StandardCharsets.UTF_8)));
393-
}
394-
395-
private final org.w3c.dom.Document doc;
396-
}
397-
398-
private static DocumentBuilder createDocumentBuilder() throws
399-
ParserConfigurationException {
400-
DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance();
401-
dbf.setFeature(
402-
"http://apache.org/xml/features/nonvalidating/load-external-dtd",
403-
false);
404-
return dbf.newDocumentBuilder();
405-
}
406-
407339
private static String getServicePListFileName(String packageName,
408340
String launcherName) {
409341
try {

0 commit comments

Comments
 (0)