blob: 16de17d2952dfac209c27ae306b2cd3c06a56c55 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.javafx2.project;
import java.awt.Color;
import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
import org.netbeans.api.java.queries.SourceForBinaryQuery;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.ant.AntArtifact;
import org.netbeans.api.project.ant.AntArtifactQuery;
import org.netbeans.modules.java.api.common.project.ProjectProperties;
import org.netbeans.modules.java.j2seproject.api.J2SEPropertyEvaluator;
import org.netbeans.modules.javafx2.platform.api.JavaFXPlatformUtils;
import org.netbeans.modules.javafx2.project.ui.JFXApplicationPanel;
import org.netbeans.modules.javafx2.project.ui.JFXPackagingPanel;
import org.netbeans.modules.javafx2.project.ui.JSEDeploymentPanel;
import org.netbeans.spi.project.support.ant.AntProjectHelper;
import org.netbeans.spi.project.support.ant.EditableProperties;
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
import org.netbeans.spi.project.support.ant.PropertyUtils;
import org.netbeans.spi.project.support.ant.ui.StoreGroup;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.MutexException;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
public final class JFXProjectProperties {
private static final Logger LOG = Logger.getLogger(JFXProjectProperties.class.getName());
private static final Color INVALID_CELL_CONTENT_COLOR = Color.RED;
public static final String JAVAFX_ENABLED = "javafx.enabled"; // NOI18N
public static final String JAVAFX_PRELOADER = "javafx.preloader"; // NOI18N
public static final String JAVAFX_SWING = "javafx.swing"; // NOI18N
public static final String JAVAFX_DISABLE_AUTOUPDATE = "javafx.disable.autoupdate"; // NOI18N
public static final String JAVAFX_DISABLE_AUTOUPDATE_NOTIFICATION = "javafx.disable.autoupdate.notification"; // NOI18N
public static final String JAVAFX_DISABLE_CONCURRENT_RUNS = "javafx.disable.concurrent.runs"; // NOI18N
public static final String JAVAFX_ENABLE_CONCURRENT_EXTERNAL_RUNS = "javafx.enable.concurrent.external.runs"; // NOI18N
public static final String JAVAFX_ENDORSED_ANT_CLASSPATH = "endorsed.javafx.ant.classpath"; // NOI18N
public static final String PLATFORM_ACTIVE = "platform.active"; // NOI18N
public static final String PLATFORM_ANT_NAME = "platform.ant.name"; //NOI18N
/** The standard extension for FXML source files. */
public static final String FXML_EXTENSION = "fxml"; // NOI18N
// copies of private J2SE properties
public static final String SOURCE_ENCODING = "source.encoding"; // NOI18N
public static final String JAVADOC_PRIVATE = "javadoc.private"; // NOI18N
public static final String JAVADOC_NO_TREE = "javadoc.notree"; // NOI18N
public static final String JAVADOC_USE = "javadoc.use"; // NOI18N
public static final String JAVADOC_NO_NAVBAR = "javadoc.nonavbar"; // NOI18N
public static final String JAVADOC_NO_INDEX = "javadoc.noindex"; // NOI18N
public static final String JAVADOC_SPLIT_INDEX = "javadoc.splitindex"; // NOI18N
public static final String JAVADOC_AUTHOR = "javadoc.author"; // NOI18N
public static final String JAVADOC_VERSION = "javadoc.version"; // NOI18N
public static final String JAVADOC_WINDOW_TITLE = "javadoc.windowtitle"; // NOI18N
public static final String JAVADOC_ENCODING = "javadoc.encoding"; // NOI18N
public static final String JAVADOC_ADDITIONALPARAM = "javadoc.additionalparam"; // NOI18N
public static final String BUILD_SCRIPT = "buildfile"; //NOI18N
public static final String DIST_JAR = "dist.jar"; // NOI18N
// Packaging properties
public static final String JAVAFX_BINARY_ENCODE_CSS = "javafx.binarycss"; // NOI18N
public static final String JAVAFX_DEPLOY_INCLUDEDT = "javafx.deploy.includeDT"; // NOI18N
public static final String JAVAFX_DEPLOY_EMBEDJNLP = "javafx.deploy.embedJNLP"; // NOI18N
public static final String JAVAFX_REBASE_LIBS = "javafx.rebase.libs"; // NOI18N
// FX config properties (Run panel), replicated from ProjectProperties
public static final String MAIN_CLASS = "javafx.main.class"; // NOI18N
//public static final String APPLICATION_ARGS = JFXProjectConfigurations.APPLICATION_ARGS;
//public static final String APP_PARAM_PREFIX = JFXProjectConfigurations.APP_PARAM_PREFIX;
//public static final String APP_PARAM_SUFFIXES[] = JFXProjectConfigurations.APP_PARAM_SUFFIXES;
public static final String RUN_JVM_ARGS = ProjectProperties.RUN_JVM_ARGS;
public static final String FALLBACK_CLASS = "javafx.fallback.class"; // NOI18N
public static final String SIGNED_JAR = "dist.signed.jar"; // NOI18N
public static final String PRELOADER_ENABLED = "javafx.preloader.enabled"; // NOI18N
public static final String PRELOADER_TYPE = "javafx.preloader.type"; // NOI18N
public static final String PRELOADER_PROJECT = "javafx.preloader.project.path"; // NOI18N
public static final String PRELOADER_CLASS = "javafx.preloader.class"; // NOI18N
public static final String PRELOADER_JAR_FILENAME = "javafx.preloader.jar.filename"; // NOI18N
public static final String PRELOADER_JAR_PATH = "javafx.preloader.jar.path"; // NOI18N
public static final String RUN_WORK_DIR = ProjectProperties.RUN_WORK_DIR; // NOI18N
public static final String RUN_APP_WIDTH = "javafx.run.width"; // NOI18N
public static final String RUN_APP_HEIGHT = "javafx.run.height"; // NOI18N
public static final String RUN_IN_HTMLTEMPLATE = "javafx.run.htmltemplate"; // NOI18N
public static final String RUN_IN_HTMLTEMPLATE_PROCESSED = "javafx.run.htmltemplate.processed"; // NOI18N
public static final String RUN_IN_BROWSER = "javafx.run.inbrowser"; // NOI18N
public static final String RUN_IN_BROWSER_PATH = "javafx.run.inbrowser.path"; // NOI18N
public static final String RUN_IN_BROWSER_ARGUMENTS = "javafx.run.inbrowser.arguments"; // NOI18N
public static final String RUN_IN_BROWSER_UNDEFINED = "undefined"; // NOI18N
public static final String RUN_AS = "javafx.run.as"; // NOI18N
public static final String DEFAULT_APP_WIDTH = "800"; // NOI18N
public static final String DEFAULT_APP_HEIGHT = "600"; // NOI18N
// Deployment properties
public static final String UPDATE_MODE_BACKGROUND = "javafx.deploy.backgroundupdate"; // NOI18N
public static final String ALLOW_OFFLINE = "javafx.deploy.allowoffline"; // NOI18N
public static final String INSTALL_PERMANENTLY = "javafx.deploy.installpermanently"; // NOI18N
public static final String ADD_DESKTOP_SHORTCUT = "javafx.deploy.adddesktopshortcut"; // NOI18N
public static final String ADD_STARTMENU_SHORTCUT = "javafx.deploy.addstartmenushortcut"; // NOI18N
public static final String ICON_FILE = "javafx.deploy.icon"; // NOI18N
public static final String NATIVE_ICON_FILE = "javafx.deploy.icon.native"; // NOI18N
public static final String SPLASH_IMAGE_FILE = "javafx.deploy.splash"; // NOI18N
public static final String PERMISSIONS_ELEVATED = "javafx.deploy.permissionselevated"; // NOI18N
public static final String DISABLE_PROXY = "javafx.deploy.disable.proxy"; // NOI18N
public static final String REQUEST_RT = "javafx.deploy.request.runtime"; // NOI18N
// Deployment - signing
public static final String JAVAFX_SIGNING_ENABLED = "javafx.signing.enabled"; //NOI18N
public static final String JAVAFX_SIGNING_TYPE = "javafx.signing.type"; //NOI18N
public static final String JAVAFX_SIGNING_KEYSTORE = "javafx.signing.keystore"; //NOI18N
public static final String JAVAFX_SIGNING_KEYSTORE_PASSWORD = "javafx.signing.keystore.password"; //NOI18N
public static final String JAVAFX_SIGNING_KEY = "javafx.signing.keyalias"; //NOI18N
public static final String JAVAFX_SIGNING_KEY_PASSWORD = "javafx.signing.keyalias.password"; //NOI18N
public static final String JAVAFX_SIGNING_BLOB = "javafx.signing.blob"; //NOI18N
// Deployment - native packaging
public static final String NATIVE_BUNDLING_ENABLED = "native.bundling.enabled"; //NOI18N
public static final String NATIVE_BUNDLING_TYPE = "native.bundling.type"; //NOI18N
//public static final String JAVASE_NATIVE_BUNDLING_ENABLED = "native.bundling.enabled"; //NOI18N
//Deloyment - copylibs
public static final String COPYLIBS_EXCLUDES = "copylibs.excludes"; //NOI18N
// Deployment - common and SE specific
public static final String RUN_CP = "run.classpath"; //NOI18N
public static final String BUILD_CLASSES = "build.classes.dir"; //NOI18N
public static final String JAVASE_KEEP_JFXRT_ON_CLASSPATH = "keep.javafx.runtime.on.classpath"; //NOI18N
// Deployment - libraries download mode
public static final String DOWNLOAD_MODE_LAZY_JARS = "download.mode.lazy.jars"; //NOI18N
private static final String DOWNLOAD_MODE_LAZY_JAR = "download.mode.lazy.jar."; //NOI18N
private static final String DOWNLOAD_MODE_LAZY_FORMAT = DOWNLOAD_MODE_LAZY_JAR +"%s"; //NOI18N
// Deployment - callbacks
public static final String JAVASCRIPT_CALLBACK_PREFIX = "javafx.jscallback."; // NOI18N
// Application
public static final String IMPLEMENTATION_VERSION = "javafx.application.implementation.version"; // NOI18N
public static final String IMPLEMENTATION_VERSION_DEFAULT = "1.0"; // NOI18N
// folders and files
public static final String PROJECT_CONFIGS_DIR = JFXProjectConfigurations.PROJECT_CONFIGS_DIR;
public static final String PROJECT_PRIVATE_CONFIGS_DIR = JFXProjectConfigurations.PROJECT_PRIVATE_CONFIGS_DIR;
public static final String PROPERTIES_FILE_EXT = JFXProjectConfigurations.PROPERTIES_FILE_EXT;
public static final String CONFIG_PROPERTIES_FILE = JFXProjectConfigurations.CONFIG_PROPERTIES_FILE;
public static final String DEFAULT_CONFIG = NbBundle.getBundle("org.netbeans.modules.javafx2.project.ui.Bundle").getString("JFXConfigurationProvider.default.label"); // NOI18N
public static final String DEFAULT_CONFIG_STANDALONE = NbBundle.getBundle("org.netbeans.modules.javafx2.project.ui.Bundle").getString("JFXConfigurationProvider.standalone.label"); // NOI18N
public static final String DEFAULT_CONFIG_WEBSTART = NbBundle.getBundle("org.netbeans.modules.javafx2.project.ui.Bundle").getString("JFXConfigurationProvider.webstart.label"); // NOI18N
public static final String DEFAULT_CONFIG_BROWSER = NbBundle.getBundle("org.netbeans.modules.javafx2.project.ui.Bundle").getString("JFXConfigurationProvider.browser.label"); // NOI18N
// explicit manifest entries (see #231951, #234231, http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/no_redeploy.html)
public static final String MANIFEST_CUSTOM_CODEBASE = "manifest.custom.codebase"; // NOI18N
public static final String MANIFEST_CUSTOM_PERMISSIONS = "manifest.custom.permissions"; // NOI18N
public static final String PLATFORM_RUNTIME = "platform.runtime"; //NOI18N
// FX RT artifact reference to be kept at compile classpath
private static final String JFX_EXTENSION_CPREF = "${javafx.classpath.extension}"; //NOI18N
private StoreGroup fxPropGroup = new StoreGroup();
// Packaging
JToggleButton.ToggleButtonModel binaryEncodeCSS;
public JToggleButton.ToggleButtonModel getBinaryEncodeCSSModel() {
return binaryEncodeCSS;
}
private JFXPackagingPanel packagingPanel = null;
public JFXPackagingPanel getPackagingPanel() {
if(packagingPanel == null) {
packagingPanel = new JFXPackagingPanel(this);
}
return packagingPanel;
}
private JFXApplicationPanel applicationPanel = null;
public JFXApplicationPanel getApplicationPanel() {
if(applicationPanel == null) {
applicationPanel = new JFXApplicationPanel(this);
}
return applicationPanel;
}
private JSEDeploymentPanel seDeploymentPanel = null;
public JSEDeploymentPanel getSEDeploymentPanel() {
if(seDeploymentPanel == null) {
seDeploymentPanel = new JSEDeploymentPanel(this);
}
return seDeploymentPanel;
}
// CustomizerRun
private JFXConfigs CONFIGS = null;
public JFXConfigs getConfigs() {
return CONFIGS;
}
private Map<String,String> browserPaths = null;
public Map<String, String> getBrowserPaths() {
return browserPaths;
}
public void resetBrowserPaths() {
this.browserPaths = new HashMap<String, String>();
}
public void setBrowserPaths(Map<String, String> browserPaths) {
this.browserPaths = browserPaths;
}
// CustomizerRun - Preloader source type
public enum PreloaderSourceType {
NONE("none"), // NOI18N
PROJECT("project"), // NOI18N
JAR("jar"); // NOI18N
private final String propertyValue;
PreloaderSourceType(String propertyValue) {
this.propertyValue = propertyValue;
}
public String getString() {
return propertyValue;
}
}
PreloaderClassComboBoxModel preloaderClassModel;
public PreloaderClassComboBoxModel getPreloaderClassModel() {
return preloaderClassModel;
}
// CustomizerRun - Run type
public enum RunAsType {
STANDALONE("standalone", DEFAULT_CONFIG_STANDALONE), // NOI18N
ASWEBSTART("webstart", DEFAULT_CONFIG_WEBSTART), // NOI18N
INBROWSER("embedded", DEFAULT_CONFIG_BROWSER); // NOI18N
private final String propertyValue;
private final String defaultConfig;
RunAsType(String propertyValue, String defaultConfig) {
this.propertyValue = propertyValue;
this.defaultConfig = defaultConfig;
}
public String getString() {
return propertyValue;
}
public String getDefaultConfig() {
return defaultConfig;
}
}
JToggleButton.ToggleButtonModel runStandalone;
JToggleButton.ToggleButtonModel runAsWebStart;
JToggleButton.ToggleButtonModel runInBrowser;
// Deployment
JToggleButton.ToggleButtonModel allowOfflineModel;
public JToggleButton.ToggleButtonModel getAllowOfflineModel() {
return allowOfflineModel;
}
JToggleButton.ToggleButtonModel backgroundUpdateCheck;
public JToggleButton.ToggleButtonModel getBackgroundUpdateCheckModel() {
return backgroundUpdateCheck;
}
JToggleButton.ToggleButtonModel installPermanently;
public JToggleButton.ToggleButtonModel getInstallPermanentlyModel() {
return installPermanently;
}
JToggleButton.ToggleButtonModel addDesktopShortcut;
public JToggleButton.ToggleButtonModel getAddDesktopShortcutModel() {
return addDesktopShortcut;
}
JToggleButton.ToggleButtonModel addStartMenuShortcut;
public JToggleButton.ToggleButtonModel getAddStartMenuShortcutModel() {
return addStartMenuShortcut;
}
JToggleButton.ToggleButtonModel disableProxy;
public JToggleButton.ToggleButtonModel getDisableProxyModel() {
return disableProxy;
}
String wsIconPath;
String splashImagePath;
String nativeIconPath;
public String getWSIconPath() {
return wsIconPath;
}
public void setWSIconPath(String path) {
this.wsIconPath = path;
}
public String getSplashImagePath() {
return splashImagePath;
}
public void setSplashImagePath(String path) {
this.splashImagePath = path;
}
public String getNativeIconPath() {
return nativeIconPath;
}
public void setNativeIconPath(String path) {
this.nativeIconPath = path;
}
// Deployment - Signing
public enum SigningType {
NOSIGN("notsigned"), // NOI18N
SELF("self"), // NOI18N
KEY("key"); // NOI18N
private final String propertyValue;
SigningType(String propertyValue) {
this.propertyValue = propertyValue;
}
public String getString() {
return propertyValue;
}
}
boolean signingEnabled;
boolean signingBlob;
SigningType signingType;
String signingKeyStore;
String signingKeyAlias;
boolean permissionsElevated;
char [] signingKeyStorePassword;
char [] signingKeyPassword;
public boolean getSigningEnabled() {
return signingEnabled;
}
public void setSigningEnabled(boolean enabled) {
this.signingEnabled = enabled;
}
public boolean getBLOBSigningEnabled() {
return signingBlob;
}
public void setBLOBSigningEnabled(boolean enabled) {
this.signingBlob = enabled;
}
public boolean getPermissionsElevated() {
return permissionsElevated;
}
public void setPermissionsElevated(boolean enabled) {
this.permissionsElevated = enabled;
}
public SigningType getSigningType() {
return signingType;
}
public void setSigningType(SigningType type) {
this.signingType = type;
}
public String getSigningKeyStore() {
return signingKeyStore;
}
public String getSigningKeyAlias() {
return signingKeyAlias;
}
public char[] getSigningKeyStorePassword() {
return signingKeyStorePassword;
}
public char[] getSigningKeyPassword() {
return signingKeyPassword;
}
public void setSigningKeyAlias(String signingKeyAlias) {
this.signingKeyAlias = signingKeyAlias;
}
public void setSigningKeyPassword(char[] signingKeyPassword) {
this.signingKeyPassword = signingKeyPassword;
}
public void setSigningKeyStore(String signingKeyStore) {
this.signingKeyStore = signingKeyStore;
}
public void setSigningKeyStorePassword(char[] signingKeyStorePassword) {
this.signingKeyStorePassword = signingKeyStorePassword;
}
// Deployment - Native Packaging (JDK 7u6+)
public enum BundlingType {
NONE("none", OS.ALL, "None"), // NOI18N
ALL("all", OS.ALL, "All Artifacts"), // NOI18N
IMAGE("image", OS.ALL, "Image Only"), // NOI18N
INSTALLER("installer", OS.ALL, "All Installers"), // NOI18N
DEB("deb", OS.LINUX, "DEB Package"), // NOI18N
RPM("rpm", OS.LINUX, "RPM Package"), // NOI18N
DMG("dmg", OS.MAC, "DMG Image"), // NOI18N
EXE("exe", OS.WIN, "EXE Installer"), // NOI18N
MSI("msi", OS.WIN, "MSI Installer"); // NOI18N
private final String propertyValue;
private final String description;
private final OS extent;
public enum OS {ALL, WIN, MAC, LINUX, NONE}
BundlingType(String propertyValue, OS os, String desc) {
this.propertyValue = propertyValue;
this.extent = os;
this.description = desc;
}
public String getValue() {
return propertyValue;
}
public OS getExtent() {
return extent;
}
@Override
public String toString() {
return description;
}
}
boolean nativeBundlingEnabled;
public boolean getNativeBundlingEnabled() {
return nativeBundlingEnabled;
}
public void setNativeBundlingEnabled(boolean enabled) {
this.nativeBundlingEnabled = enabled;
}
// in SE project - keep jfxrt.jar on classpath
boolean keepJFXRTonCP;
public boolean getKeepJFXRTonCP() {
return keepJFXRTonCP;
}
public void setKeepJFXRTonCP(boolean enabled) {
this.keepJFXRTonCP = enabled;
}
// Deployment - Libraries Download Mode
List<? extends File> runtimeCP;
List<? extends File> lazyJars;
boolean lazyJarsChanged;
public List<? extends File> getRuntimeCP() {
return runtimeCP;
}
public List<? extends File> getLazyJars() {
return lazyJars;
}
public void setLazyJars(List<? extends File> newLazyJars) {
this.lazyJars = newLazyJars;
}
public boolean getLazyJarsChanged() {
return lazyJarsChanged;
}
public void setLazyJarsChanged(boolean changed) {
this.lazyJarsChanged = changed;
}
// Deployment - JavaScript Callbacks
Map<String,String> jsCallbacks;
boolean jsCallbacksChanged;
public Map<String,String> getJSCallbacks() {
return jsCallbacks;
}
public void setJSCallbacks(Map<String,String> newCallbacks) {
jsCallbacks = newCallbacks;
}
public boolean getJSCallbacksChanged() {
return jsCallbacksChanged;
}
public void setJSCallbacksChanged(boolean changed) {
jsCallbacksChanged = changed;
}
// Deployment - requested RT
String requestedRT;
public String getRequestedRT() {
return requestedRT;
}
public void setRequestedRT(String rt) {
this.requestedRT = rt;
}
// Application
String implVersion;
public String getImplementationVersion() {
return implVersion;
}
public void setImplementationVersion(String implVer) {
implVersion = implVer;
}
// Project related references
private J2SEPropertyEvaluator j2sePropEval;
private PropertyEvaluator evaluator;
private Project project;
public Project getProject() {
return project;
}
public PropertyEvaluator getEvaluator() {
return evaluator;
}
/** Keeps singleton instance of JFXProjectProperties for any fx project for which property customizer is opened at once */
private static Map<String, JFXProjectProperties> propInstance = new HashMap<String, JFXProjectProperties>();
/** Keeps set of category markers used to identify validity of JFXProjectProperties instance */
private Set<String> instanceMarkers = new TreeSet<String>();
public void markInstance(@NonNull String marker) {
instanceMarkers.add(marker);
}
public boolean isInstanceMarked(@NonNull String marker) {
return instanceMarkers.contains(marker);
}
/** Factory method */
public static JFXProjectProperties getInstance(Lookup context) {
Project proj = context.lookup(Project.class);
String projDir = proj.getProjectDirectory().getPath();
JFXProjectProperties prop = propInstance.get(projDir);
if(prop == null) {
prop = new JFXProjectProperties(context);
propInstance.put(projDir, prop);
}
return prop;
}
/** Factory method
* This is to prevent reuse of the same instance after the properties dialog
* has been cancelled. Called by each FX category provider at the time
* when properties dialog is opened, it checks/stores category-specific marker strings.
* Previous existence of marker string indicates that properties dialog had been opened
* before and ended by Cancel, otherwise this instance would not exist (OK would
* cause properties to be saved and the instance deleted by a call to JFXProjectProperties.cleanup()).
* (Note that this is a workaround to avoid adding listener to properties dialog close event.)
*
* @param category marker string to indicate which category provider is calling this
* @return instance of JFXProjectProperties shared among category panels in the current Project Properties dialog only
*
* @deprecated handle cleanup using ProjectCustomizer.Category.setCloseListener instead
*/
@Deprecated
public static JFXProjectProperties getInstancePerSession(Lookup context, String category) {
Project proj = context.lookup(Project.class);
String projDir = proj.getProjectDirectory().getPath();
JFXProjectProperties prop = propInstance.get(projDir);
if(prop != null) {
if(prop.isInstanceMarked(category)) {
// category marked before - create new instance to avoid reuse after Cancel
prop = null;
} else {
prop.markInstance(category);
}
}
if(prop == null) {
prop = new JFXProjectProperties(context);
propInstance.put(projDir, prop);
prop.markInstance(category);
}
return prop;
}
/** Getter method */
public static JFXProjectProperties getInstanceIfExists(Project proj) {
assert proj != null;
String projDir = proj.getProjectDirectory().getPath();
JFXProjectProperties prop = propInstance.get(projDir);
if(prop != null) {
return prop;
}
return null;
}
/** Getter method */
public static JFXProjectProperties getInstanceIfExists(Lookup context) {
Project proj = context.lookup(Project.class);
return getInstanceIfExists(proj);
}
public static void cleanup(Lookup context) {
Project proj = context.lookup(Project.class);
String projDir = proj.getProjectDirectory().getPath();
propInstance.remove(projDir);
}
/** Keeps singleton instance of a set of preloader artifact dependencies for any fx project */
private static Map<String, Set<PreloaderArtifact>> prelArtifacts = new HashMap<String, Set<PreloaderArtifact>>();
/** Factory method */
private static Set<PreloaderArtifact> getPreloaderArtifacts(@NonNull Project proj) {
String projDir = proj.getProjectDirectory().getPath();
Set<PreloaderArtifact> prels = prelArtifacts.get(projDir);
if(prels == null) {
prels = new HashSet<PreloaderArtifact>();
prelArtifacts.put(projDir, prels);
}
return prels;
}
public String getFXRunTimeJar() {
assert evaluator != null;
String active = evaluator.getProperty(PLATFORM_ACTIVE);
JavaPlatform platform = JavaFXPlatformUtils.findJavaPlatform(active);
if(platform != null) {
return JavaFXPlatformUtils.getJavaFXRuntimeJar(platform);
}
return null;
}
/** Creates a new instance of JFXProjectProperties */
private JFXProjectProperties(Lookup context) {
//defaultInstance = provider.getJFXProjectProperties();
project = context.lookup(Project.class);
if (project != null) {
j2sePropEval = project.getLookup().lookup(J2SEPropertyEvaluator.class);
evaluator = j2sePropEval.evaluator();
// Packaging
binaryEncodeCSS = fxPropGroup.createToggleButtonModel(evaluator, JAVAFX_BINARY_ENCODE_CSS); // set true by default in JFXProjectGenerator
// Deployment
allowOfflineModel = fxPropGroup.createToggleButtonModel(evaluator, ALLOW_OFFLINE); // set true by default in JFXProjectGenerator
backgroundUpdateCheck = fxPropGroup.createToggleButtonModel(evaluator, UPDATE_MODE_BACKGROUND); // set true by default in JFXProjectGenerator
installPermanently = fxPropGroup.createToggleButtonModel(evaluator, INSTALL_PERMANENTLY);
addDesktopShortcut = fxPropGroup.createToggleButtonModel(evaluator, ADD_DESKTOP_SHORTCUT);
addStartMenuShortcut = fxPropGroup.createToggleButtonModel(evaluator, ADD_STARTMENU_SHORTCUT);
disableProxy = fxPropGroup.createToggleButtonModel(evaluator, DISABLE_PROXY);
// CustomizerRun
CONFIGS = new JFXConfigs();
CONFIGS.read();
initPreloaderArtifacts(project, CONFIGS);
CONFIGS.setActive(evaluator.getProperty(ProjectProperties.PROP_PROJECT_CONFIGURATION_CONFIG));
preloaderClassModel = new PreloaderClassComboBoxModel();
initVersion(evaluator);
initIcons(evaluator);
initSigning(evaluator);
initNativeBundling(evaluator);
initResources(evaluator, project, CONFIGS);
initJSCallbacks(evaluator);
initRest(evaluator);
}
}
public static boolean isTrue(final String value) {
return value != null &&
(value.equalsIgnoreCase("true") || //NOI18N
value.equalsIgnoreCase("yes") || //NOI18N
value.equalsIgnoreCase("on")); //NOI18N
}
public static boolean isNonEmpty(String s) {
return s != null && !s.isEmpty();
}
public static boolean isEqual(final String s1, final String s2) {
return (s1 == null && s2 == null) ||
(s1 != null && s2 != null && s1.equals(s2));
}
public static boolean isEqualIgnoreCase(final String s1, final String s2) {
return (s1 == null && s2 == null) ||
(s1 != null && s2 != null && s1.equalsIgnoreCase(s2));
}
public static boolean isEqualText(final String s1, final String s2) {
return ((s1 == null || s1.isEmpty()) && (s2 == null || s2.isEmpty())) ||
(s1 != null && s2 != null && s1.equals(s2));
}
/**
* Used to display named and unnamed parameters in table. The point
* here is to keep consistency by forbidding invalid parameters, i.e.,
* named parameters with value but without name.
*/
public static class PropertiesTableModel extends AbstractTableModel {
private List<Map<String,String>> properties;
private List<Map<String,String>> defaultProperties;
private String propSuffixes[];
private String columnNames[];
public PropertiesTableModel(List<Map<String,String>> props, List<Map<String,String>> defaultProps, String sfxs[], String clmns[]) {
if (sfxs.length < clmns.length) {
throw new IllegalArgumentException();
}
properties = props;
defaultProperties = defaultProps;
propSuffixes = sfxs;
columnNames = clmns;
}
public boolean isValid() {
assert properties != null;
for(Map<String,String> map : properties) {
String left = map.get(propSuffixes[0]);
if(left == null || left.trim().isEmpty()) {
for(int c=1; c<columnNames.length; c++) {
String right = map.get(propSuffixes[c]);
if(right != null && !right.isEmpty()) {
return false;
}
}
}
}
return true;
}
public boolean hasDefaultProperties() {
return defaultProperties != null;
}
public boolean isRowEmpty(int index) {
if(!properties.isEmpty() && index < properties.size()) {
Map<String,String> last = properties.get(index);
for(int c=0; c<columnNames.length; c++) {
String value = last.get(propSuffixes[c]);
if(value != null && !value.isEmpty()) {
return false;
}
}
return true;
}
return false;
}
public boolean isLastRowEmpty() {
return isRowEmpty(properties.size()-1);
}
@Override
public int getRowCount() {
return properties.size();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
if(columnIndex>0) {
String left = properties.get(rowIndex).get(propSuffixes[columnIndex-1]);
if(left == null || left.isEmpty()) {
return false;
}
}
return true;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
properties.get(rowIndex).put(propSuffixes[columnIndex], (String) aValue);
fireTableCellUpdated(rowIndex, columnIndex);
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return properties.get(rowIndex).get(propSuffixes[columnIndex]);
}
/**
* restore defaults if defaultProperties exist, otherwise clean
*/
public void reset() {
if(defaultProperties != null) {
properties.clear();
properties.addAll(defaultProperties);
} else {
properties.clear();
}
fireTableDataChanged();
}
/**
* Indicates whether it makes sense to enable the Default/Clean
* button, i.e., whether current table contents differ from the default
* @return true is a call to reset() would modify data
*/
public boolean isResettable() {
if(hasDefaultProperties()) {
return !areEqual(properties, defaultProperties);
}
return !properties.isEmpty();
}
private boolean areEqual(List<Map<String,String>> list1, List<Map<String,String>> list2) {
String s1 = getAsString(list1);
String s2 = getAsString(list2);
if(isEqualText(s1, s2)) {
return true;
}
return false;
}
private String getAsString(List<Map<String,String>> list) {
if(list != null) {
List<String> l = new LinkedList<String>();
for(Map<String, String> entry : list) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < columnNames.length; i++) {
sb.append(propSuffixes[i]);
sb.append(entry.get(propSuffixes[i]));
}
l.add(sb.toString());
}
Collections.sort(l);
return l.toString();
}
return null;
}
public void addRow() {
Map<String,String> emptyMap = new HashMap<String,String>();
for (String suffix : propSuffixes) {
emptyMap.put(suffix, "");
}
properties.add(emptyMap);
fireTableDataChanged();
}
public void removeRow(int index) {
properties.remove(index);
fireTableDataChanged();
}
private int getEmptyRow() {
for(int i = 0; i < properties.size(); i++) {
if(isRowEmpty(i)) {
return i;
}
}
return -1;
}
public void removeEmptyRows() {
boolean removed = false;
while(true) {
int i = getEmptyRow();
if(i == -1) {
if(removed) {
fireTableDataChanged();
}
return;
}
removeRow(i);
removed = true;
}
}
}
/**
* May not be necessary but improves user experience - updates OK button state
* immediately while editing text in table, i.e., not only after cell editing
* has finished.
*/
public static class PropertyCellEditor extends DefaultCellEditor implements CellEditorListener {
private DocumentListener listener = null;
private Document document = null;
public PropertyCellEditor() {
super(new JTextField());
}
public void registerCellEditorListener() {
addCellEditorListener(this);
}
public void unregisterCellEditorListener() {
removeCellEditorListener(this);
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if(document != null) {
document.removeDocumentListener(listener);
}
JTextField editor = (JTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);
document = editor.getDocument();
listener = new CellEditorDocumentListener(table.getModel(), row, column);
document.addDocumentListener(listener);
return editor;
}
@Override
public void editingStopped(ChangeEvent e) {
removeDocumentListenerReference();
}
@Override
public void editingCanceled(ChangeEvent e) {
removeDocumentListenerReference();
}
private void removeDocumentListenerReference() {
if(document != null) {
document.removeDocumentListener(listener);
document = null;
}
listener = null;
}
private class CellEditorDocumentListener implements DocumentListener {
private TableModel model;
private int row;
private int column;
private CellEditorDocumentListener(TableModel model, int row, int column) {
this.model = model;
this.row = row;
this.column = column;
}
@Override
public void insertUpdate(DocumentEvent e) {
update(e);
}
@Override
public void removeUpdate(DocumentEvent e) {
update(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
update(e);
}
private void update(DocumentEvent e) {
Document d = e.getDocument();
try {
model.setValueAt(d.getText(0, d.getLength()), row, column);
} catch (BadLocationException ex) {
// can be ignored
}
}
}
}
/**
* Content in invalid cells is rendered in emphasized color.
*/
public static class PropertyCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object o, boolean isSelected, boolean hasFocus, int row, int column) {
Component cell = super.getTableCellRendererComponent(table, o, isSelected, hasFocus, row, column);
if(!table.getModel().isCellEditable(row, column)) {
cell.setForeground(INVALID_CELL_CONTENT_COLOR);
} else {
cell.setForeground(UIManager.getColor("textText")); //NOI18N
}
return cell;
}
}
private FileObject getSrcRoot(@NonNull Project project)
{
FileObject srcRoot = null;
for (SourceGroup sg : ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
if (!isTest(sg.getRootFolder(),project)) {
srcRoot = sg.getRootFolder();
break;
}
}
return srcRoot;
}
private void initPreloaderArtifacts(@NonNull Project project, @NonNull JFXConfigs configs) {
Set<PreloaderArtifact> prels = getPreloaderArtifacts(project);
prels.clear();
try {
prels.addAll(getPreloaderArtifactsFromConfigs(configs));
} catch (IOException ex) {
// can be ignored
}
}
public boolean hasPreloaderInAnyConfig() {
return hasPreloaderInAnyConfig(CONFIGS);
}
private boolean hasPreloaderInAnyConfig(@NonNull JFXConfigs configs) {
if(configs != null) {
for(String config : configs.getConfigNames()) {
if(isTrue( configs.getProperty(config, PRELOADER_ENABLED))) {
return true;
}
}
}
return false;
}
private PreloaderArtifact getPreloaderArtifactFromConfig(@NonNull JFXConfigs configs, @NonNull String config, boolean transparent) throws IOException {
// check records on any type of preloader from config
if(configs.hasConfig(config)) {
PreloaderArtifact preloader = null;
if(!isTrue( transparent ? configs.getPropertyTransparent(config, PRELOADER_ENABLED) : configs.getProperty(config, PRELOADER_ENABLED))) {
return null;
}
String prelTypeString = transparent ? configs.getPropertyTransparent(config, PRELOADER_TYPE) : configs.getProperty(config, PRELOADER_TYPE);
String prelProjDir = transparent ? configs.getPropertyTransparent(config, PRELOADER_PROJECT) : configs.getProperty(config, PRELOADER_PROJECT);
if (prelProjDir != null && isEqualIgnoreCase(prelTypeString, PreloaderSourceType.PROJECT.getString())) {
FileObject thisProjDir = project.getProjectDirectory();
FileObject fo = JFXProjectUtils.getFileObject(thisProjDir, prelProjDir);
File prelProjDirF = (fo == null) ? null : FileUtil.toFile(fo);
if( isTrue(transparent ? configs.getPropertyTransparent(config, PRELOADER_ENABLED) : configs.getProperty(config, PRELOADER_ENABLED)) && prelProjDirF != null && prelProjDirF.exists() ) {
FileObject srcRoot = getSrcRoot(getProject());
if(srcRoot != null) {
prelProjDirF = FileUtil.normalizeFile(prelProjDirF);
FileObject prelProjFO = FileUtil.toFileObject(prelProjDirF);
final Project proj = ProjectManager.getDefault().findProject(prelProjFO);
AntArtifact[] artifacts = proj != null ? AntArtifactQuery.findArtifactsByType(proj, JavaProjectConstants.ARTIFACT_TYPE_JAR) : new AntArtifact[0];
List<URI> allURI = new ArrayList<URI>();
for(AntArtifact artifact : artifacts) {
allURI.addAll(Arrays.asList(artifact.getArtifactLocations()));
}
if(!allURI.isEmpty()) {
URI[] arrayURI = allURI.toArray(new URI[0]);
preloader = new PreloaderProjectArtifact(artifacts, arrayURI, srcRoot, ClassPath.COMPILE, prelProjDirF.getAbsolutePath());
}
}
}
}
if(preloader == null) {
String prelJar = transparent ? configs.getPropertyTransparent(config, PRELOADER_JAR_PATH) : configs.getProperty(config, PRELOADER_JAR_PATH);
if(prelJar != null && isEqualIgnoreCase(prelTypeString, PreloaderSourceType.JAR.getString())) {
FileObject thisProjDir = project.getProjectDirectory();
FileObject fo = JFXProjectUtils.getFileObject(thisProjDir, prelJar);
File prelJarF = (fo == null) ? null : FileUtil.toFile(fo);
if( prelJarF != null && prelJarF.exists() ) {
FileObject srcRoot = getSrcRoot(getProject());
if(srcRoot != null) {
URL[] urls = new URL[1];
urls[0] = FileUtil.urlForArchiveOrDir(prelJarF);
FileObject[] fos = new FileObject[1];
fos[0] = FileUtil.toFileObject(prelJarF);
preloader = new PreloaderJarArtifact(urls, fos, srcRoot, ClassPath.COMPILE, urls[0].toString());
}
}
}
}
return preloader;
}
return null;
}
private Set<PreloaderArtifact> getPreloaderArtifactsFromConfigs(@NonNull JFXConfigs configs) throws IOException {
Set<PreloaderArtifact> preloaderArtifacts = new HashSet<PreloaderArtifact>();
// check records on all preloaders from all configurations
for(String config : configs.getConfigNames()) {
PreloaderArtifact preloader = getPreloaderArtifactFromConfig(configs, config, false);
if(preloader != null) {
preloaderArtifacts.add(preloader);
}
}
return preloaderArtifacts;
}
public void updatePreloaderDependencies() {
try {
updatePreloaderDependencies(CONFIGS);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
private void updatePreloaderDependencies(@NonNull final JFXConfigs configs) throws IOException {
// depeding on the currently (de)selected preloaders update project dependencies,
// i.e., remove disabled/deleted preloader project dependencies and add enabled/added preloader project dependencies
Set<PreloaderArtifact> preloaderArtifacts = getPreloaderArtifacts(getProject());
for(PreloaderArtifact artifact : preloaderArtifacts) {
artifact.setValid(false);
}
final PreloaderArtifact preloaderActive = getPreloaderArtifactFromConfig(configs, configs.getActive(), true);
// collect all dependencies from any configuration
for(final String config : configs.getConfigNames()) {
final PreloaderArtifact preloader = getPreloaderArtifactFromConfig(configs, config, false);
if(preloader != null) {
boolean updated = false;
for(PreloaderArtifact a : preloaderArtifacts) {
if(a.equals(preloader)) {
a.setValid(true);
updated = true;
}
}
if(!updated) {
preloader.setValid(true);
preloaderArtifacts.add(preloader);
}
}
}
// remove all dependencies not-specified in active configuration (and add the active one)
Set<PreloaderArtifact> toRemove = new HashSet<PreloaderArtifact>();
for(final PreloaderArtifact artifact : preloaderArtifacts) {
if(preloaderActive == null || !preloaderActive.equals(artifact)) {
toRemove.add(artifact);
}
}
if(preloaderActive != null || !toRemove.isEmpty()) {
final Set<PreloaderArtifact> toRemoveFinal = Collections.unmodifiableSet(toRemove);
ProjectManager.mutex().postWriteRequest(new Runnable() {
@Override
public void run() {
if(preloaderActive != null) {
try {
preloaderActive.addDependency();
} catch(IOException e) {
LOG.log(Level.SEVERE, "Preloader dependency addition failed."); // NOI18N
}
}
try {
for(final PreloaderArtifact artifact : toRemoveFinal) {
artifact.removeDependency();
}
} catch(IOException e) {
LOG.log(Level.SEVERE, "Preloader dependency removal failed."); // NOI18N
}
}
});
}
// remove from preloaderArtifacts those not more present in any config
toRemove.clear();
for(final PreloaderArtifact artifact : preloaderArtifacts) {
if(!artifact.isValid()) {
toRemove.add(artifact);
}
}
for(PreloaderArtifact artifact : toRemove) {
preloaderArtifacts.remove(artifact);
}
}
private static boolean isTest(final @NonNull FileObject root, final @NonNull Project project) {
assert root != null;
assert project != null;
final ClassPath cp = ClassPath.getClassPath(root, ClassPath.COMPILE);
for (ClassPath.Entry entry : cp.entries()) {
final FileObject[] srcRoots = SourceForBinaryQuery.findSourceRoots(entry.getURL()).getRoots();
for (FileObject srcRoot : srcRoots) {
if (project.equals(FileOwnerQuery.getOwner(srcRoot))) {
return true;
}
}
}
return false;
}
private void storeRest(@NonNull EditableProperties editableProps, @NonNull EditableProperties privProps) {
// create extended manifest attribute properties if not existing
if(!editableProps.containsKey(MANIFEST_CUSTOM_CODEBASE) && !privProps.containsKey(MANIFEST_CUSTOM_CODEBASE)) {
editableProps.setProperty(MANIFEST_CUSTOM_CODEBASE, "*"); // NOI18N
editableProps.setComment(MANIFEST_CUSTOM_CODEBASE, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_manifest_custom_codebase")}, false); // NOI18N
}
if(!editableProps.containsKey(MANIFEST_CUSTOM_PERMISSIONS) && !privProps.containsKey(MANIFEST_CUSTOM_PERMISSIONS)) {
editableProps.setProperty(MANIFEST_CUSTOM_PERMISSIONS, ""); // NOI18N
editableProps.setComment(MANIFEST_CUSTOM_PERMISSIONS, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_manifest_custom_permissions")}, false); // NOI18N
}
// store implementation version
setOrRemove(editableProps, IMPLEMENTATION_VERSION, implVersion);
// store signing info
editableProps.setProperty(JAVAFX_SIGNING_ENABLED, signingEnabled ? "true" : "false"); //NOI18N
editableProps.setProperty(JAVAFX_SIGNING_BLOB, signingBlob ? "true" : "false"); //NOI18N
editableProps.setProperty(JAVAFX_SIGNING_TYPE, signingType.getString());
setOrRemove(editableProps, JAVAFX_SIGNING_KEY, signingKeyAlias);
setOrRemove(editableProps, JAVAFX_SIGNING_KEYSTORE, signingKeyStore);
editableProps.setProperty(PERMISSIONS_ELEVATED, permissionsElevated ? "true" : "false"); //NOI18N
setOrRemove(privProps, JAVAFX_SIGNING_KEYSTORE_PASSWORD, signingKeyStorePassword);
setOrRemove(privProps, JAVAFX_SIGNING_KEY_PASSWORD, signingKeyPassword);
// store native bundling info
editableProps.setProperty(NATIVE_BUNDLING_ENABLED, nativeBundlingEnabled ? "true" : "false"); //NOI18N
// store icons
setOrRemove(editableProps, ICON_FILE, wsIconPath);
setOrRemove(editableProps, SPLASH_IMAGE_FILE, splashImagePath);
setOrRemove(editableProps, NATIVE_ICON_FILE, nativeIconPath);
// store requested RT
setOrRemove(editableProps, REQUEST_RT, requestedRT);
// store resources
storeResources(editableProps);
// store JavaScript callbacks
storeJSCallbacks(editableProps);
}
private void setOrRemove(EditableProperties props, String name, char [] value) {
setOrRemove(props, name, value != null ? new String(value) : null);
}
private void setOrRemove(@NonNull EditableProperties props, @NonNull String name, String value) {
if (value != null) {
props.setProperty(name, value);
} else {
props.remove(name);
}
}
public void store() throws IOException {
String fxEnabled = evaluator.getProperty(JFXProjectProperties.JAVAFX_ENABLED);
if(isTrue(fxEnabled)) {
storeFX();
} else {
storeSE();
}
}
private void storeFX() throws IOException {
updatePreloaderDependencies(CONFIGS);
CONFIGS.storeActive();
final EditableProperties ep = new EditableProperties(true);
final FileObject projPropsFO = project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
final EditableProperties pep = new EditableProperties(true);
final FileObject privPropsFO = project.getProjectDirectory().getFileObject(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
try {
ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction<Void>() {
@Override
public Void run() throws Exception {
final InputStream is = projPropsFO.getInputStream();
final InputStream pis = privPropsFO.getInputStream();
try {
ep.load(is);
} finally {
if (is != null) {
is.close();
}
}
try {
pep.load(pis);
} finally {
if (pis != null) {
pis.close();
}
}
fxPropGroup.store(ep);
storeRest(ep, pep);
CONFIGS.store(ep, pep);
updatePreloaderComment(ep);
//JFXProjectUtils.updateClassPathExtensionProperties(ep);
logProps(ep);
OutputStream os = null;
FileLock lock = null;
try {
lock = projPropsFO.lock();
os = projPropsFO.getOutputStream(lock);
ep.store(os);
} finally {
if (lock != null) {
lock.releaseLock();
}
if (os != null) {
os.close();
}
}
try {
lock = privPropsFO.lock();
os = privPropsFO.getOutputStream(lock);
pep.store(os);
} finally {
if (lock != null) {
lock.releaseLock();
}
if (os != null) {
os.close();
}
}
return null;
}
});
} catch (MutexException mux) {
throw (IOException) mux.getException();
}
}
private void storeSE() throws IOException {
final EditableProperties ep = new EditableProperties(true);
final FileObject projPropsFO = project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
try {
final InputStream is = projPropsFO.getInputStream();
ProjectManager.mutex().readAccess(new Mutex.ExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try {
ep.load(is);
} finally {
if (is != null) {
is.close();
}
}
return null;
}
});
} catch (MutexException mux) {
throw (IOException) mux.getException();
}
setOrRemove(ep, JAVASE_KEEP_JFXRT_ON_CLASSPATH, keepJFXRTonCP ? "true" : null); //NOI18N
//Copylibs excludes for J2SE Project with JFX extension
String copyLibsExcludes = ep.getProperty(COPYLIBS_EXCLUDES);
if (keepJFXRTonCP) {
if (copyLibsExcludes == null || copyLibsExcludes.isEmpty()) {
copyLibsExcludes = JFX_EXTENSION_CPREF;
} else if (copyLibsExcludes.indexOf(JFX_EXTENSION_CPREF)<0){
copyLibsExcludes = copyLibsExcludes + ':' + JFX_EXTENSION_CPREF;
}
setOrRemove(ep, COPYLIBS_EXCLUDES, copyLibsExcludes);
} else {
if (copyLibsExcludes != null) {
copyLibsExcludes = JFXProjectUtils.removeFromPath(copyLibsExcludes,JFX_EXTENSION_CPREF);
if (copyLibsExcludes.isEmpty()) {
copyLibsExcludes = null;
}
}
setOrRemove(ep, COPYLIBS_EXCLUDES, copyLibsExcludes);
}
//JFXProjectUtils.updateClassPathExtensionProperties(ep);
try {
ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction<Void>() {
@Override
public Void run() throws Exception {
OutputStream os = null;
FileLock lock = null;
try {
lock = projPropsFO.lock();
os = projPropsFO.getOutputStream(lock);
ep.store(os);
} finally {
if (lock != null) {
lock.releaseLock();
}
if (os != null) {
os.close();
}
}
return null;
}
});
} catch (MutexException mux) {
throw (IOException) mux.getException();
}
}
private void updatePreloaderComment(EditableProperties ep) {
if(isTrue(ep.get(JFXProjectProperties.PRELOADER_ENABLED))) {
ep.setComment(JFXProjectProperties.PRELOADER_ENABLED, new String[]{"# " + NbBundle.getMessage(JFXProjectProperties.class, "COMMENT_use_preloader")}, false); // NOI18N
} else {
ep.setComment(JFXProjectProperties.PRELOADER_ENABLED, new String[]{"# " + NbBundle.getMessage(JFXProjectProperties.class, "COMMENT_dontuse_preloader")}, false); // NOI18N
}
}
private void initVersion(PropertyEvaluator eval) {
implVersion = eval.getProperty(IMPLEMENTATION_VERSION);
if(implVersion == null) {
implVersion = IMPLEMENTATION_VERSION_DEFAULT;
}
}
private void initIcons(PropertyEvaluator eval) {
wsIconPath = eval.getProperty(ICON_FILE);
splashImagePath = eval.getProperty(SPLASH_IMAGE_FILE);
nativeIconPath = eval.getProperty(NATIVE_ICON_FILE);
}
private void initSigning(PropertyEvaluator eval) {
String enabled = eval.getProperty(JAVAFX_SIGNING_ENABLED);
String blob = eval.getProperty(JAVAFX_SIGNING_BLOB);
String signedProp = eval.getProperty(JAVAFX_SIGNING_TYPE);
signingEnabled = isTrue(enabled);
signingBlob = isTrue(blob);
if(signedProp == null) {
signingType = SigningType.NOSIGN;
} else {
if(signedProp.equalsIgnoreCase(SigningType.SELF.getString())) {
signingType = SigningType.SELF;
} else {
if(signedProp.equalsIgnoreCase(SigningType.KEY.getString())) {
signingType = SigningType.KEY;
} else {
signingType = SigningType.NOSIGN;
}
}
}
signingKeyStore = eval.getProperty(JAVAFX_SIGNING_KEYSTORE);
//if (signingKeyStore == null) signingKeyStore = "";
signingKeyAlias = eval.getProperty(JAVAFX_SIGNING_KEY);
//if (signingKeyAlias == null) signingKeyAlias = "";
if (eval.getProperty(JAVAFX_SIGNING_KEYSTORE_PASSWORD) != null) {
signingKeyStorePassword = eval.getProperty(JAVAFX_SIGNING_KEYSTORE_PASSWORD).toCharArray();
}
if (eval.getProperty(JAVAFX_SIGNING_KEY_PASSWORD) != null) {
signingKeyPassword = eval.getProperty(JAVAFX_SIGNING_KEY_PASSWORD).toCharArray();
}
permissionsElevated = isTrue(eval.getProperty(PERMISSIONS_ELEVATED));
}
private void initNativeBundling(PropertyEvaluator eval) {
String fxEnabled = evaluator.getProperty(JFXProjectProperties.JAVAFX_ENABLED);
if(isTrue(fxEnabled)) {
String enabled = eval.getProperty(NATIVE_BUNDLING_ENABLED);
nativeBundlingEnabled = isTrue(enabled);
}
}
private void initRest(PropertyEvaluator eval) {
requestedRT = eval.getProperty(REQUEST_RT);
keepJFXRTonCP = isTrue(eval.getProperty(JAVASE_KEEP_JFXRT_ON_CLASSPATH));
}
private boolean isParentOf(File parent, File child) {
if(parent == null || child == null) {
return false;
}
if(!parent.exists() || !child.exists()) {
return false;
}
FileObject parentFO = FileUtil.toFileObject(parent);
FileObject childFO = FileUtil.toFileObject(child);
return FileUtil.isParentOf(parentFO, childFO);
}
private void initResources (final PropertyEvaluator eval, final Project prj, final JFXConfigs configs) {
final String lz = eval.getProperty(DOWNLOAD_MODE_LAZY_JARS); //old way, when changed rewritten to new
final String rcp = eval.getProperty(RUN_CP);
final String bc = eval.getProperty(BUILD_CLASSES);
final String plat = eval.getProperty(PLATFORM_ACTIVE);
JavaPlatform platform = JavaFXPlatformUtils.findJavaPlatform(plat);
final File prjDir = FileUtil.toFile(prj.getProjectDirectory());
final File bcDir = bc == null ? null : PropertyUtils.resolveFile(prjDir, bc);
final List<File> lazyFileList = new ArrayList<File>();
String[] paths;
if (lz != null) {
paths = PropertyUtils.tokenizePath(lz);
for (String p : paths) {
lazyFileList.add(PropertyUtils.resolveFile(prjDir, p));
}
}
paths = rcp != null ? PropertyUtils.tokenizePath(rcp) : new String[0];
String mainJar = eval.getProperty(DIST_JAR);
final File mainFile = mainJar != null ? PropertyUtils.resolveFile(prjDir, mainJar) : null;
List<FileObject> preloaders = new ArrayList<FileObject>();
try {
for(PreloaderArtifact pa : getPreloaderArtifactsFromConfigs(configs)) {
preloaders.addAll(Arrays.asList(pa.getFileObjects()));
}
} catch (IOException ex) {
// no need to react
}
Collection<FileObject> platfF = platform != null ? platform.getInstallFolders() : null;
final List<File> resFileList = new ArrayList<File>(paths.length);
for (String p : paths) {
if (p.startsWith("${") && p.endsWith("}")) { //NOI18N
continue;
}
final File f = PropertyUtils.resolveFile(prjDir, p);
if (!f.exists()) {
continue;
}
if (mainFile != null && f.equals(mainFile)) {
continue;
}
if(platfF != null) {
boolean cont = false;
for(FileObject fo : platfF) {
if(isParentOf(FileUtil.toFile(fo), f)) {
cont = true;
}
}
if(cont) {
continue;
}
}
boolean isPrel = false;
for(FileObject prelfo : preloaders) {
File prelf = FileUtil.toFile(prelfo);
if(prelf != null && prelf.equals(f)) {
isPrel = true;
continue;
}
}
if (!isPrel && (bc == null || !bcDir.equals(f)) ) {
resFileList.add(f);
if (isTrue(eval.getProperty(String.format(DOWNLOAD_MODE_LAZY_FORMAT, f.getName())))) {
lazyFileList.add(f);
}
}
}
lazyJars = lazyFileList;
runtimeCP = resFileList;
lazyJarsChanged = false;
}
private void storeResources(final EditableProperties props) {
if (lazyJarsChanged) {
//Remove old way if exists
props.remove(DOWNLOAD_MODE_LAZY_JARS);
final Iterator<Map.Entry<String,String>> it = props.entrySet().iterator();
while (it.hasNext()) {
if (it.next().getKey().startsWith(DOWNLOAD_MODE_LAZY_JAR)) {
it.remove();
}
}
for (File lazyJar : lazyJars) {
props.setProperty(String.format(DOWNLOAD_MODE_LAZY_FORMAT, lazyJar.getName()), "true"); //NOI18N
}
}
}
private void initJSCallbacks (final PropertyEvaluator eval) {
String platformName = eval.getProperty(PLATFORM_ACTIVE);
Map<String,List<String>/*|null*/> callbacks = JFXProjectUtils.getJSCallbacks(platformName);
Map<String,String/*|null*/> result = new LinkedHashMap<String,String/*|null*/>();
for(Map.Entry<String,List<String>/*|null*/> entry : callbacks.entrySet()) {
String v = eval.getProperty(JFXProjectProperties.JAVASCRIPT_CALLBACK_PREFIX + entry.getKey());
if(v != null && !v.isEmpty()) {
result.put(entry.getKey(), v);
}
}
jsCallbacks = result;
jsCallbacksChanged = false;
}
private void storeJSCallbacks(final EditableProperties props) {
if (jsCallbacksChanged && jsCallbacks != null) {
for (Map.Entry<String,String> entry : jsCallbacks.entrySet()) {
if(entry.getValue() != null && !entry.getValue().isEmpty()) {
props.setProperty(JAVASCRIPT_CALLBACK_PREFIX + entry.getKey(), entry.getValue()); //NOI18N
} else {
props.remove(JAVASCRIPT_CALLBACK_PREFIX + entry.getKey());
}
}
}
}
public class PreloaderClassComboBoxModel extends DefaultComboBoxModel {
private volatile boolean filling = false;
private ChangeListener changeListener = null;
public PreloaderClassComboBoxModel() {
fillNoPreloaderAvailable();
}
public void addChangeListener (ChangeListener l) {
changeListener = l;
}
public void removeChangeListener (ChangeListener l) {
changeListener = null;
}
public final void fillNoPreloaderAvailable() {
removeAllElements();
addElement(NbBundle.getMessage(JFXProjectProperties.class, "MSG_ComboNoPreloaderClassAvailable")); // NOI18N
}
public void fillFromProject(final Project project, final String select, final JFXConfigs configs, final String activeConfig) {
final Collection<? extends FileObject> roots = JFXProjectUtils.getClassPathMap(project).keySet();
RequestProcessor.getDefault().post(new Runnable() {
@Override
public void run() {
if(!filling) {
filling = true;
removeAllElements();
if(project == null) {
addElement(NbBundle.getMessage(JFXProjectProperties.class, "MSG_ComboNoPreloaderClassAvailable")); // NOI18N
return;
}
final Set<String> appClassNames = JFXProjectUtils.getAppClassNames(roots, "javafx.application.Preloader"); //NOI18N
if(appClassNames.isEmpty()) {
addElement(NbBundle.getMessage(JFXProjectProperties.class, "MSG_ComboNoPreloaderClassAvailable")); // NOI18N
} else {
addElements(appClassNames);
if(select != null) {
setSelectedItem(select);
}
String verify = (String)getSelectedItem();
if(!isEqual(configs.getPropertyTransparent(activeConfig, JFXProjectProperties.PRELOADER_CLASS), verify)) {
configs.setPropertyTransparent(activeConfig, JFXProjectProperties.PRELOADER_CLASS, verify);
}
}
if (changeListener != null) {
changeListener.stateChanged (appClassNames.isEmpty() ? null : new ChangeEvent (this));
}
filling = false;
}
}
});
}
public void fillFromJAR(final FileObject jarFile, final JFXProjectProperties fxProps, final String select, final JFXConfigs configs, final String activeConfig) {
RequestProcessor.getDefault().post(new Runnable() {
@Override
public void run() {
if(!filling) {
filling = true;
removeAllElements();
if(jarFile == null) {
addElement(NbBundle.getMessage(JFXProjectProperties.class, "MSG_ComboNoPreloaderClassAvailable")); // NOI18N
return;
}
final Set<String> appClassNames = JFXProjectUtils.getAppClassNamesInJar(jarFile, "javafx.application.Preloader", fxProps.getFXRunTimeJar()); //NOI18N
appClassNames.remove("com.javafx.main.Main"); // NOI18N
appClassNames.remove("com.javafx.main.NoJavaFXFallback"); // NOI18N
if(appClassNames.isEmpty()) {
addElement(NbBundle.getMessage(JFXProjectProperties.class, "MSG_ComboNoPreloaderClassAvailable")); // NOI18N
} else {
addElements(appClassNames);
if(select != null) {
setSelectedItem(select);
}
String verify = (String)getSelectedItem();
if(!isEqual(configs.getPropertyTransparent(activeConfig, JFXProjectProperties.PRELOADER_CLASS), verify)) {
configs.setPropertyTransparent(activeConfig, JFXProjectProperties.PRELOADER_CLASS, verify);
}
}
if (changeListener != null) {
changeListener.stateChanged (appClassNames.isEmpty() ? null : new ChangeEvent (this));
}
filling = false;
}
}
});
}
private void addElements(Set<String> elems) {
for (String elem : elems) {
addElement(elem);
}
}
}
/**
* Each preloader specified in project configurations needs
* to be added/removed to/from project dependencies whenever
* configurations change (see Run category in Project Properties
* dialog).
* List of preoader artifacts is thus needed to keep track which
* project dependencies are preloader related.
*/
abstract class PreloaderArtifact {
/**
* Dependency validity tag
*/
private boolean valid;
/**
* Add {@code this} to dependencies of project if it is not there yet
* @return true if preloader artifact has been added, false if it was already there
*/
abstract boolean addDependency() throws IOException, UnsupportedOperationException;
/**
* Remove {@code this} from dependencies of project if it is there
* @return true if preloader artifact has been removed, false if it was not among project dependencies
*/
abstract boolean removeDependency() throws IOException, UnsupportedOperationException;
/**
* Returns array of files represented by this PreloaderArtifact
* @return array of FileObjects of files represented by this object
*/
abstract FileObject[] getFileObjects();
/**
* Set the validity tag for {@code this} artifact
* @param valid true for dependencies to be kept, false for dependencies to be removed
*/
void setValid(boolean valid) {
this.valid = valid;
}
/**
* Get the validity tag for {@code this} artifact
* @return valid true for dependencies to be kept, false for dependencies to be removed
*/
boolean isValid() {
return valid;
}
}
class PreloaderProjectArtifact extends PreloaderArtifact {
private final String ID;
private final AntArtifact[] artifacts;
private final URI[] artifactElements;
private final FileObject projectArtifact;
private final String classPathType;
PreloaderProjectArtifact(final @NonNull AntArtifact[] artifacts, final @NonNull URI[] artifactElements,
final @NonNull FileObject projectArtifact, final @NonNull String classPathType, final @NonNull String ID) {
this.artifacts = artifacts;
this.artifactElements = artifactElements;
this.projectArtifact = projectArtifact;
this.classPathType = classPathType;
this.ID = ID;
}
@Override
public boolean addDependency() throws IOException, UnsupportedOperationException {
return ProjectClassPathModifier.addAntArtifacts(artifacts, artifactElements, projectArtifact, classPathType);
}
@Override
public boolean removeDependency() throws IOException, UnsupportedOperationException {
return ProjectClassPathModifier.removeAntArtifacts(artifacts, artifactElements, projectArtifact, classPathType);
}
@Override
public boolean equals(Object that){
if ( this == that ) return true;
if ( !(that instanceof PreloaderProjectArtifact) ) return false;
PreloaderProjectArtifact concrete = (PreloaderProjectArtifact)that;
return ID.equals(concrete.ID);
}
@Override
final FileObject[] getFileObjects() {
List<FileObject> l = new ArrayList<FileObject>();
for(AntArtifact a : artifacts) {
l.addAll(Arrays.asList(a.getArtifactFiles()));
}
return l.toArray(new FileObject[l.size()]);
}
}
class PreloaderJarArtifact extends PreloaderArtifact {
private final String ID;
private final URL[] classPathRoots;
private final FileObject[] fileObjects;
private final FileObject projectArtifact;
private final String classPathType;
PreloaderJarArtifact(final @NonNull URL[] classPathRoots, final @NonNull FileObject[] fileObjects, final @NonNull FileObject projectArtifact,
final @NonNull String classPathType, final @NonNull String ID) {
this.classPathRoots = classPathRoots;
this.fileObjects = fileObjects;
this.projectArtifact = projectArtifact;
this.classPathType = classPathType;
this.ID = ID;
}
@Override
public boolean addDependency() throws IOException, UnsupportedOperationException {
return ProjectClassPathModifier.addRoots(classPathRoots, projectArtifact, classPathType);
}
@Override
public boolean removeDependency() throws IOException, UnsupportedOperationException {
return ProjectClassPathModifier.removeRoots(classPathRoots, projectArtifact, classPathType);
}
@Override
public boolean equals(Object that){
if ( this == that ) return true;
if ( !(that instanceof PreloaderJarArtifact) ) return false;
PreloaderJarArtifact concrete = (PreloaderJarArtifact)that;
return ID.equals(concrete.ID);
}
@Override
final FileObject[] getFileObjects() {
return fileObjects;
}
}
/**
* Project configurations maintenance class
*
* Getter/Setter naming conventions:
* "Property" in method name -> method deals with single properties in configuration given by parameter config
* "Default" in method name -> method deals with properties in default configuration
* "Active" in method name -> method deals with properties in currently chosen configuration
* "Transparent" in method name -> method deals with property in configuration fiven by parameter config if
* exists, or with property in default configuration otherwise. This is to provide simple access to
* union of default and non-default properties that are to be presented to users in non-default configurations
* "Param" in method name -> metod deals with properties representing sets of application parameters
*/
public class JFXConfigs extends JFXProjectConfigurations {
// property groups
private String PRELOADER_GROUP_NAME = "preloader"; // NOI18N
private List<String> PRELOADER_PROPERTIES = Arrays.asList(new String[] {
JFXProjectProperties.PRELOADER_ENABLED, JFXProjectProperties.PRELOADER_TYPE, JFXProjectProperties.PRELOADER_PROJECT,
JFXProjectProperties.PRELOADER_JAR_PATH, JFXProjectProperties.PRELOADER_JAR_FILENAME, JFXProjectProperties.PRELOADER_CLASS});
private String BROWSER_GROUP_NAME = "browser"; // NOI18N
private List<String> BROWSER_PROPERTIES = Arrays.asList(new String[] {
JFXProjectProperties.RUN_IN_BROWSER, JFXProjectProperties.RUN_IN_BROWSER_PATH, JFXProjectProperties.RUN_IN_BROWSER_ARGUMENTS});
public final List<String> getPreloaderProperties() {
return Collections.unmodifiableList(PRELOADER_PROPERTIES);
}
public final List<String> getBrowserProperties() {
return Collections.unmodifiableList(BROWSER_PROPERTIES);
}
JFXConfigs() {
super(project.getProjectDirectory());
registerProjectProperties(new String[] {
ProjectProperties.MAIN_CLASS, MAIN_CLASS, /*APPLICATION_ARGS,*/ RUN_JVM_ARGS,
PRELOADER_ENABLED, PRELOADER_TYPE, PRELOADER_PROJECT, PRELOADER_JAR_PATH, PRELOADER_JAR_FILENAME, PRELOADER_CLASS,
RUN_WORK_DIR, RUN_APP_WIDTH, RUN_APP_HEIGHT, RUN_IN_HTMLTEMPLATE, RUN_IN_BROWSER, RUN_IN_BROWSER_PATH, RUN_AS});
registerPrivateProperties(new String[] {
RUN_WORK_DIR, RUN_IN_HTMLTEMPLATE, RUN_IN_BROWSER, RUN_IN_BROWSER_PATH, RUN_IN_BROWSER_ARGUMENTS, RUN_AS});
registerStaticProperties(new String[] {
RUN_AS});
Map<String, String> substituteMissing = new HashMap<String, String>();
substituteMissing.put(RUN_APP_WIDTH, DEFAULT_APP_WIDTH);
substituteMissing.put(RUN_APP_HEIGHT, DEFAULT_APP_HEIGHT);
registerDefaultsIfMissing(substituteMissing);
registerCleanEmptyProjectProperties(new String[] {
MAIN_CLASS, RUN_JVM_ARGS,
PRELOADER_ENABLED, PRELOADER_TYPE, PRELOADER_PROJECT, PRELOADER_JAR_PATH, PRELOADER_JAR_FILENAME, PRELOADER_CLASS,
RUN_APP_WIDTH, RUN_APP_HEIGHT});
registerCleanEmptyPrivateProperties(new String[] {
RUN_WORK_DIR, RUN_IN_HTMLTEMPLATE, RUN_IN_BROWSER, RUN_IN_BROWSER_PATH});
defineGroup(PRELOADER_GROUP_NAME, getPreloaderProperties());
defineGroup(BROWSER_GROUP_NAME, getBrowserProperties());
}
}
static void logProps(EditableProperties ep) {
LOG.log(Level.INFO, PRELOADER_ENABLED + " = " + (ep.get(PRELOADER_ENABLED)==null ? "null" : ep.get(PRELOADER_ENABLED)));
LOG.log(Level.INFO, PRELOADER_TYPE + " = " + (ep.get(PRELOADER_TYPE)==null ? "null" : ep.get(PRELOADER_TYPE)));
LOG.log(Level.INFO, PRELOADER_PROJECT + " = " + (ep.get(PRELOADER_PROJECT)==null ? "null" : ep.get(PRELOADER_PROJECT)));
LOG.log(Level.INFO, PRELOADER_CLASS + " = " + (ep.get(PRELOADER_CLASS)==null ? "null" : ep.get(PRELOADER_CLASS)));
LOG.log(Level.INFO, PRELOADER_JAR_FILENAME + " = " + (ep.get(PRELOADER_JAR_FILENAME)==null ? "null" : ep.get(PRELOADER_JAR_FILENAME)));
LOG.log(Level.INFO, PRELOADER_JAR_PATH + " = " + (ep.get(PRELOADER_JAR_PATH)==null ? "null" : ep.get(PRELOADER_JAR_PATH)));
}
}