Skip to content

Commit

Permalink
Add support for bundle URL types in macOS launcher in product files
Browse files Browse the repository at this point in the history
Adds CFBundleURLTypes entries to the Info.plist dictionary.

Contributes to
eclipse-platform/eclipse.platform.ui#1901.
  • Loading branch information
sratz authored and HannesWell committed Aug 12, 2024
1 parent e1b554d commit 7d87cd1
Show file tree
Hide file tree
Showing 14 changed files with 327 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2005, 2012 IBM Corporation and others.
* Copyright (c) 2005, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -11,16 +11,19 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Code 9 - Additional function and fixes
* SAP SE - support macOS bundle URL types
*******************************************************************************/
package org.eclipse.equinox.internal.p2.publisher.eclipse;

import java.io.*;
import java.nio.file.Files;
import java.util.List;
import javax.xml.transform.TransformerException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.publisher.eclipse.IMacOsBundleUrlType;
import org.eclipse.pde.internal.publishing.Activator;
import org.eclipse.pde.internal.publishing.Utils;
import org.eclipse.pde.internal.swt.tools.IconExe;
Expand All @@ -38,6 +41,7 @@ public class BrandingIron {
private boolean brandIcons = true;
private String id;
private Version version;
private List<IMacOsBundleUrlType> macOsBundleUrlTypes = List.of();

public BrandingIron() {
}
Expand All @@ -60,6 +64,10 @@ public void setIcons(String[] value) {
icons = (value == null || value.length == 0) ? null : value;
}

public void setMacOsBundleUrlTypes(List<IMacOsBundleUrlType> value) {
macOsBundleUrlTypes = value;
}

public void setIcons(String value) {
icons = org.eclipse.equinox.internal.frameworkadmin.utils.Utils.getTokens(value, ",");//$NON-NLS-1$
if (icons[0].startsWith("${")) { //$NON-NLS-1$
Expand Down Expand Up @@ -562,6 +570,10 @@ private void modifyInfoPListFile(ExecutablesDescriptor descriptor, File initialR
infoPListEditor.setKey(InfoPListEditor.ICON_KEY, iconName);
}

for (IMacOsBundleUrlType macOsBundleUrlType : macOsBundleUrlTypes) {
infoPListEditor.addCfBundleUrlType(macOsBundleUrlType.getScheme(), macOsBundleUrlType.getName());
}

File target = new File(targetRoot, "Info.plist"); //$NON-NLS-1$ ;
try {
target.getParentFile().mkdirs();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008, 2014 Code 9 and others.
* Copyright (c) 2008, 2024 Code 9 and others.
*
* This
* program and the accompanying materials are made available under the terms of
Expand All @@ -14,6 +14,7 @@
* EclipseSource - ongoing development
* SAP AG - ongoing development
* Rapicorp - additional features
* SAP SE - support macOS bundle URL types
******************************************************************************/
package org.eclipse.equinox.internal.p2.publisher.eclipse;

Expand All @@ -22,6 +23,7 @@
import java.util.Map;
import org.eclipse.equinox.frameworkadmin.BundleInfo;
import org.eclipse.equinox.p2.metadata.IVersionedId;
import org.eclipse.equinox.p2.publisher.eclipse.IMacOsBundleUrlType;
import org.eclipse.equinox.p2.repository.IRepositoryReference;

/**
Expand Down Expand Up @@ -207,4 +209,14 @@ public interface IProductDescriptor {
*/
public String getVM(String os);

/**
* Returns a list of URI schemes handled by the product.
* <p>
* Currently, these are only evaluated on macOS, since they need to be placed in
* the Information Property List file (<code>Info.plist</code>).
*/
public default List<IMacOsBundleUrlType> getMacOsBundleUrlTypes() {
return List.of();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2023 Rapicorp, Inc and others.
* Copyright (c) 2015, 2024 Rapicorp, Inc and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -10,6 +10,7 @@
*
* Contributors:
* Rapicorp, Inc. - initial API and implementation
* SAP SE - support macOS bundle URL types
*******************************************************************************/
package org.eclipse.equinox.internal.p2.publisher.eclipse;

Expand All @@ -34,6 +35,7 @@ public class InfoPListEditor {
public static final String BUNDLE_INFO_KEY = "CFBundleGetInfoString"; //$NON-NLS-1$
public static final String BUNDLE_VERSION_KEY = "CFBundleVersion"; //$NON-NLS-1$
public static final String BUNDLE_SHORT_VERSION_KEY = "CFBundleShortVersionString"; //$NON-NLS-1$
public static final String BUNDLE_URL_TYPES = "CFBundleURLTypes"; //$NON-NLS-1$
public static final String ICON_KEY = "CFBundleIconFile"; //$NON-NLS-1$

private final Element infoPList;
Expand Down Expand Up @@ -108,15 +110,57 @@ public void setKey(String key, String value) {
}

private void addKey(String key, String value) throws DOMException, XPathExpressionException {
Element keyNode = document.createElement("key"); //$NON-NLS-1$
Text keyName = document.createTextNode(key);
keyNode.appendChild(keyName);
Node dict = getNode(infoPList, "/plist/dict"); //$NON-NLS-1$
appendTextContentChild(dict, "key", key); //$NON-NLS-1$
appendTextContentChild(dict, "string", value); //$NON-NLS-1$
}

public void addCfBundleUrlType(String scheme, String displayName) {
if (scheme == null) {
throw new IllegalArgumentException("Scheme can't be null"); //$NON-NLS-1$
}
if (displayName == null) {
throw new IllegalArgumentException("Display Name can't be null"); //$NON-NLS-1$
}
Node bundleUrlTypesNode = getOrCreateCfBundleUrlTypesArray();
Element dict = appendNewChild(bundleUrlTypesNode, "dict"); //$NON-NLS-1$
{
appendTextContentChild(dict, "key", "CFBundleURLName"); //$NON-NLS-1$ //$NON-NLS-2$
appendTextContentChild(dict, "string", displayName); //$NON-NLS-1$
}
{
appendTextContentChild(dict, "key", "CFBundleURLSchemes"); //$NON-NLS-1$ //$NON-NLS-2$
Element array = appendNewChild(dict, "array"); //$NON-NLS-1$
appendTextContentChild(array, "string", scheme); //$NON-NLS-1$
}
}

private Node getOrCreateCfBundleUrlTypesArray() {
try {
String expression = String.format("/plist/dict/key[text() = '%s']/following-sibling::array[1]", //$NON-NLS-1$
BUNDLE_URL_TYPES);
Node arrayNode = getNode(infoPList, expression);
if (arrayNode == null) {
Node dict = getNode(infoPList, "/plist/dict"); //$NON-NLS-1$
appendTextContentChild(dict, "key", "CFBundleURLTypes"); //$NON-NLS-1$ //$NON-NLS-2$
arrayNode = appendNewChild(dict, "array"); //$NON-NLS-1$
}
return arrayNode;
} catch (XPathExpressionException e) {
// Can't happen since we craft the expression carefully
throw new IllegalStateException(e);
}
}

Element appendNewChild(Node parent, String name) {
Element node = document.createElement(name);
parent.appendChild(node);
return node;
}

Element stringNode = document.createElement("string"); //$NON-NLS-1$
Text stringValue = document.createTextNode(value);
stringNode.appendChild(stringValue);
getNode(infoPList, "/plist/dict").appendChild(keyNode); //$NON-NLS-1$
getNode(infoPList, "/plist/dict").appendChild(stringNode); //$NON-NLS-1$
private void appendTextContentChild(Node parent, String elementName, String text) {
Element keyNode = appendNewChild(parent, elementName);
keyNode.appendChild(document.createTextNode(text));
}

private XPath getXPathTool() {
Expand Down Expand Up @@ -176,12 +220,9 @@ public List<String> getEclipseArguments() {
public void setEclipseArguments(List<String> arguments) {
try {
removeNodes(infoPList, "/plist/dict/key[text() = 'Eclipse']/following-sibling::array[1]/string"); //$NON-NLS-1$
Node parent = getNode(infoPList, "/plist/dict/key[text() = 'Eclipse']/following-sibling::array[1]"); //$NON-NLS-1$
for (String arg : arguments) {
Element stringNode = document.createElement("string"); //$NON-NLS-1$
Text stringName = document.createTextNode(arg);
stringNode.appendChild(stringName);
Node toAppendTo = getNode(infoPList, "/plist/dict/key[text() = 'Eclipse']/following-sibling::array[1]"); //$NON-NLS-1$
toAppendTo.appendChild(stringNode);
appendTextContentChild(parent, "string", arg); //$NON-NLS-1$
}
} catch (XPathExpressionException e) {
//can't happen
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2024 SAP SE and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* SAP SE - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.p2.publisher.eclipse;

import org.eclipse.equinox.p2.publisher.eclipse.IMacOsBundleUrlType;

record MacOsBundleUrlType(String scheme, String name) implements IMacOsBundleUrlType {

@Override
public String getScheme() {
return scheme();
}

@Override
public String getName() {
return name();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2005, 2023 IBM Corporation and others.
* Copyright (c) 2005, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -16,6 +16,7 @@
* SAP AG - ongoing development
* Rapicorp - additional features
* Red Hat Inc. - Bug 460967
* SAP SE - support macOS bundle URL types
*******************************************************************************/

package org.eclipse.equinox.internal.p2.publisher.eclipse;
Expand All @@ -32,6 +33,7 @@
import org.eclipse.equinox.p2.metadata.IVersionedId;
import org.eclipse.equinox.p2.metadata.VersionedId;
import org.eclipse.equinox.p2.publisher.eclipse.FeatureEntry;
import org.eclipse.equinox.p2.publisher.eclipse.IMacOsBundleUrlType;
import org.eclipse.equinox.p2.repository.IRepository;
import org.eclipse.equinox.p2.repository.IRepositoryReference;
import org.eclipse.equinox.p2.repository.spi.RepositoryReference;
Expand Down Expand Up @@ -65,6 +67,8 @@ public class ProductFile extends DefaultHandler implements IProductDescriptor {
private static final String ATTRIBUTE_ARCH = "arch"; //$NON-NLS-1$
private static final String ATTRIBUTE_ENABLED = "enabled"; //$NON-NLS-1$
private static final String ATTRIBUTE_FEATURE_INSTALL_MODE = "installMode"; //$NON-NLS-1$
private static final String ATTRIBUTE_MACOS_BUNDLE_URL_TYPE_SCHEME = "scheme"; //$NON-NLS-1$
private static final String ATTRIBUTE_MACOS_BUNDLE_URL_TYPE_NAME = "name"; //$NON-NLS-1$

private static final String PROPERTY_ECLIPSE_APPLICATION = "eclipse.application"; //$NON-NLS-1$
private static final String PROPERTY_ECLIPSE_PRODUCT = "eclipse.product"; //$NON-NLS-1$
Expand Down Expand Up @@ -134,6 +138,8 @@ public class ProductFile extends DefaultHandler implements IProductDescriptor {
private static final String EL_ARCH_SPARC = "argsSPARC"; //$NON-NLS-1$
private static final String EL_REPOSITORIES = "repositories"; //$NON-NLS-1$
private static final String EL_REPOSITORY = "repository"; //$NON-NLS-1$
private static final String EL_BUNDLE_URL_TYPES = "bundleUrlTypes"; //$NON-NLS-1$
private static final String EL_BUNDLE_URL_TYPE = "bundleUrlType"; //$NON-NLS-1$

//These constants form a small state machine to parse the .product file
private static final int STATE_START = 0;
Expand Down Expand Up @@ -169,6 +175,8 @@ public class ProductFile extends DefaultHandler implements IProductDescriptor {
private static final int STATE_VM_LINUX = 31;
private static final int STATE_VM_MACOS = 32;
private static final int STATE_VM_WINDOWS = 33;
private static final int STATE_LAUNCHER_MAC = 34;
private static final int STATE_LAUNCHER_MAC_BUNDLE_URL_TYPES = 35;

private static final String PI_PDEBUILD = "org.eclipse.pde.build"; //$NON-NLS-1$
private final static int EXCEPTION_PRODUCT_FORMAT = 23;
Expand Down Expand Up @@ -207,6 +215,7 @@ public class ProductFile extends DefaultHandler implements IProductDescriptor {
private final String currentOS;
private final List<IRepositoryReference> repositories = new ArrayList<>();
private final Map<String, String> vms = new HashMap<>();
private final List<IMacOsBundleUrlType> macOsBundleUrlTypes = new ArrayList<>();

private static String normalize(String text) {
if (text == null || text.trim().length() == 0)
Expand Down Expand Up @@ -802,6 +811,7 @@ public void startElement(String uri, String localName, String qName, Attributes
break;
case OS_MACOSX:
processMac(attributes);
state = STATE_LAUNCHER_MAC;
break;
default:
break;
Expand All @@ -813,8 +823,24 @@ public void startElement(String uri, String localName, String qName, Attributes
}
break;

case STATE_LAUNCHER_MAC:
if (null != localName) {
if (EL_BUNDLE_URL_TYPES.equals(localName)) {
state = STATE_LAUNCHER_MAC_BUNDLE_URL_TYPES;
}
}
break;

case STATE_LAUNCHER_ARGS :
case STATE_LAUNCHER_MAC_BUNDLE_URL_TYPES:
if (null != localName) {
if (EL_BUNDLE_URL_TYPE.equals(localName)) {
processMacOsBundleUrlTypes(attributes);
state = STATE_LAUNCHER_MAC_BUNDLE_URL_TYPES;
}
}
break;

case STATE_LAUNCHER_ARGS:
if (null != localName) switch (localName) {
case PROGRAM_ARGS:
state = STATE_PROGRAM_ARGS;
Expand Down Expand Up @@ -1084,6 +1110,16 @@ public void endElement(String uri, String localName, String qName) {
if (EL_LICENSE.equals(localName))
state = STATE_PRODUCT;
break;
case STATE_LAUNCHER_MAC_BUNDLE_URL_TYPES:
if (EL_BUNDLE_URL_TYPES.equals(localName)) {
state = STATE_LAUNCHER_MAC;
}
break;
case STATE_LAUNCHER_MAC:
if (OS_MACOSX.equals(localName)) {
state = STATE_LAUNCHER;
}
break;
case STATE_VM :
state = STATE_PRODUCT;
break;
Expand Down Expand Up @@ -1376,8 +1412,18 @@ private void processMac(Attributes attributes) {
addIcon(OS_MACOSX, attributes.getValue(ATTRIBUTE_ICON));
}

private void processMacOsBundleUrlTypes(Attributes attributes) {
macOsBundleUrlTypes.add(new MacOsBundleUrlType(attributes.getValue(ATTRIBUTE_MACOS_BUNDLE_URL_TYPE_SCHEME),
attributes.getValue(ATTRIBUTE_MACOS_BUNDLE_URL_TYPE_NAME)));
}

@Override
public ProductContentType getProductContentType() {
return productContentType;
}

@Override
public List<IMacOsBundleUrlType> getMacOsBundleUrlTypes() {
return macOsBundleUrlTypes;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008, 2017 Code 9 and others.
* Copyright (c) 2008, 2024 Code 9 and others.
*
* This
* program and the accompanying materials are made available under the terms of
Expand All @@ -13,6 +13,7 @@
* Code 9 - initial API and implementation
* IBM - ongoing development
* Pascal Rapicault - Support for bundled macosx http://bugs.eclipse.org/57349
* SAP SE - support macOS bundle URL types
******************************************************************************/
package org.eclipse.equinox.p2.publisher.eclipse;

Expand Down Expand Up @@ -254,6 +255,7 @@ protected void fullBrandExecutables(ExecutablesDescriptor descriptor, IBrandingA
iron.setId(idBase);
iron.setVersion(version);
iron.setIcons(advice.getIcons());
iron.setMacOsBundleUrlTypes(advice.getMacOsBundleUrlTypes());
String name = advice.getExecutableName();
if (name == null)
name = "eclipse"; //$NON-NLS-1$
Expand Down
Loading

0 comments on commit 7d87cd1

Please sign in to comment.