package jdk.jpackage.internal;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.NoSuchFileException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
import static jdk.jpackage.internal.StandardBundlerParam.ADD_LAUNCHERS;
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
public class AppImageFile {
private final String creatorVersion;
private final String creatorPlatform;
private final String launcherName;
private final List<String> addLauncherNames;
private final static String FILENAME = ".jpackage.xml";
private final static Map<Platform, String> PLATFORM_LABELS = Map.of(
Platform.LINUX, "linux", Platform.WINDOWS, "windows", Platform.MAC,
"macOS");
private AppImageFile() {
this(null, null, null, null);
}
private AppImageFile(String launcherName, List<String> addLauncherNames,
String creatorVersion, String creatorPlatform) {
this.launcherName = launcherName;
this.addLauncherNames = addLauncherNames;
this.creatorVersion = creatorVersion;
this.creatorPlatform = creatorPlatform;
}
List<String> getAddLauncherNames() {
return addLauncherNames;
}
String getLauncherName() {
return launcherName;
}
void verifyCompatible() throws ConfigException {
}
public static Path getPathInAppImage(Path appImageDir) {
return ApplicationLayout.platformAppImage()
.resolveAt(appImageDir)
.appDirectory()
.resolve(FILENAME);
}
static void save(Path appImageDir, Map<String, Object> params)
throws IOException {
IOUtils.createXml(getPathInAppImage(appImageDir), xml -> {
xml.writeStartElement("jpackage-state");
xml.writeAttribute("version", getVersion());
xml.writeAttribute("platform", getPlatform());
xml.writeStartElement("app-version");
xml.writeCharacters(VERSION.fetchFrom(params));
xml.writeEndElement();
xml.writeStartElement("main-launcher");
xml.writeCharacters(APP_NAME.fetchFrom(params));
xml.writeEndElement();
List<Map<String, ? super Object>> addLaunchers =
ADD_LAUNCHERS.fetchFrom(params);
for (int i = 0; i < addLaunchers.size(); i++) {
Map<String, ? super Object> sl = addLaunchers.get(i);
xml.writeStartElement("add-launcher");
xml.writeCharacters(APP_NAME.fetchFrom(sl));
xml.writeEndElement();
}
});
}
static AppImageFile load(Path appImageDir) throws IOException {
try {
Document doc = readXml(appImageDir);
XPath xPath = XPathFactory.newInstance().newXPath();
String mainLauncher = xpathQueryNullable(xPath,
"/jpackage-state/main-launcher/text()", doc);
if (mainLauncher == null) {
return new AppImageFile();
}
List<String> addLaunchers = new ArrayList<>();
String platform = xpathQueryNullable(xPath,
"/jpackage-state/@platform", doc);
String version = xpathQueryNullable(xPath,
"/jpackage-state/@version", doc);
NodeList launcherNameNodes = (NodeList) xPath.evaluate(
"/jpackage-state/add-launcher/text()", doc,
XPathConstants.NODESET);
for (int i = 0; i != launcherNameNodes.getLength(); i++) {
addLaunchers.add(launcherNameNodes.item(i).getNodeValue());
}
AppImageFile file = new AppImageFile(
mainLauncher, addLaunchers, version, platform);
if (!file.isValid()) {
file = new AppImageFile();
}
return file;
} catch (XPathExpressionException ex) {
throw new RuntimeException(ex);
}
}
public static Document readXml(Path appImageDir) throws IOException {
try {
Path path = getPathInAppImage(appImageDir);
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newDefaultInstance();
dbf.setFeature(
"http://apache.org/xml/features/nonvalidating/load-external-dtd",
false);
DocumentBuilder b = dbf.newDocumentBuilder();
return b.parse(Files.newInputStream(path));
} catch (ParserConfigurationException | SAXException ex) {
throw new IOException(ex);
}
}
static List<String> getLauncherNames(Path appImageDir,
Map<String, ? super Object> params) {
List<String> launchers = new ArrayList<>();
try {
AppImageFile appImageInfo = AppImageFile.load(appImageDir);
if (appImageInfo != null) {
launchers.add(appImageInfo.getLauncherName());
launchers.addAll(appImageInfo.getAddLauncherNames());
return launchers;
}
} catch (NoSuchFileException nsfe) {
Log.info(MessageFormat.format(I18N.getString(
"warning.foreign-app-image"), appImageDir));
} catch (IOException ioe) {
Log.verbose(ioe);
Log.info(MessageFormat.format(I18N.getString(
"warning.invalid-app-image"), appImageDir));
}
launchers.add(APP_NAME.fetchFrom(params));
ADD_LAUNCHERS.fetchFrom(params).stream().map(APP_NAME::fetchFrom).forEach(
launchers::add);
return launchers;
}
public static String (Path appImageDir) {
try {
return AppImageFile.load(appImageDir).getLauncherName();
} catch (IOException ioe) {
Log.verbose(MessageFormat.format(I18N.getString(
"warning.foreign-app-image"), appImageDir));
return null;
}
}
private static String xpathQueryNullable(XPath xPath, String xpathExpr,
Document xml) throws XPathExpressionException {
NodeList nodes = (NodeList) xPath.evaluate(xpathExpr, xml,
XPathConstants.NODESET);
if (nodes != null && nodes.getLength() > 0) {
return nodes.item(0).getNodeValue();
}
return null;
}
private static String getVersion() {
return System.getProperty("java.version");
}
private static String getPlatform() {
return PLATFORM_LABELS.get(Platform.getPlatform());
}
private boolean isValid() {
if (launcherName == null || launcherName.length() == 0 ||
addLauncherNames.indexOf("") != -1) {
return false;
}
return true;
}
}