blob: d8fae8a44761c6de6d6d22afe616015ea4165ae1 [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.java.j2sedeploy;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.CRC32;
import org.netbeans.api.actions.Closable;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ant.AntBuildExtender;
import org.netbeans.modules.java.j2seproject.api.J2SEPropertyEvaluator;
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.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.MutexException;
import org.openide.util.Parameters;
/**
* Java SE Deployment panel Project Properties support
*
* @author Petr Somol
* @author Tomas Zezula
* @since java.j2seproject 1.65
*/
public final class J2SEDeployProperties {
// Deployment - native packaging
public static final String NATIVE_BUNDLING_ENABLED = "native.bundling.enabled"; //NOI18N
// copied from JFXProjectProperties
public static final String JAVAFX_ENABLED = "javafx.enabled"; // NOI18N
private static final String J2SEDEPLOY_EXTENSION = "j2sedeploy"; //NOI18N
private static final String[] OLD_J2SEDEPLOY_EXTENSION = new String[0];
private static final String BUILD_SCRIPT_PROTOTYPE = String.format(
"%s/resources/build-native-prototype.xml", //NOI18N
J2SEDeployProperties.class.getPackage().getName().replace('.','/')); //NOI18N
private static final String EXTENSION_BUILD_SCRIPT_PATH = "nbproject/build-native.xml"; //NOI18N
private static final Logger LOG = Logger.getLogger(J2SEDeployProperties.class.getName());
// Project related references
private J2SEPropertyEvaluator j2sePropEval;
private PropertyEvaluator evaluator;
private Project project;
public Project getProject() {
return project;
}
public PropertyEvaluator getEvaluator() {
return evaluator;
}
boolean nativeBundlingEnabled;
public boolean getNativeBundlingEnabled() {
return nativeBundlingEnabled;
}
public void setNativeBundlingEnabled(boolean enabled) {
this.nativeBundlingEnabled = enabled;
}
/** Keeps singleton instance of J2SEDeployProperties for SE project with Deployment category open */
private static Map<String, J2SEDeployProperties> propInstance = new HashMap<String, J2SEDeployProperties>();
/** Factory method */
public static J2SEDeployProperties getInstance(Lookup context) {
Project proj = context.lookup(Project.class);
String projDir = proj.getProjectDirectory().getPath();
J2SEDeployProperties prop = propInstance.get(projDir);
if(prop == null) {
prop = new J2SEDeployProperties(context);
propInstance.put(projDir, prop);
}
return prop;
}
/** Getter method */
public static J2SEDeployProperties getInstanceIfExists(Project proj) {
assert proj != null;
String projDir = proj.getProjectDirectory().getPath();
J2SEDeployProperties prop = propInstance.get(projDir);
if(prop != null) {
return prop;
}
return null;
}
/** Getter method */
public static J2SEDeployProperties 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);
}
static String getCurrentExtensionName() {
return J2SEDEPLOY_EXTENSION;
}
static String[] getOldExtensionNames() {
return OLD_J2SEDEPLOY_EXTENSION;
}
static void updateJ2SEDeployExtension(@NonNull final Project project) throws IOException {
Parameters.notNull("project", project); //NOI18N
final FileObject buildNativeXml = copyBuildNativeTemplate(project);
assert buildNativeXml != null;
final AntBuildExtender extender = project.getLookup().lookup(AntBuildExtender.class);
assert extender != null;
modifyBuildXml(extender, buildNativeXml);
}
static boolean isBuildNativeUpToDate(@NonNull final Project project) {
Parameters.notNull("project", project); //NOI18N
final FileObject buildNativeXml = project.getProjectDirectory().getFileObject(EXTENSION_BUILD_SCRIPT_PATH);
if (buildNativeXml == null) {
return false;
}
try (
final InputStream buildIn = new BufferedInputStream(buildNativeXml.getInputStream());
final InputStream protoIn = new BufferedInputStream(J2SEDeployProperties.class.getClassLoader().getResourceAsStream(BUILD_SCRIPT_PROTOTYPE))) {
final long buildNativeCRC = computeCRC(buildIn);
final long prototypeCRC = computeCRC(protoIn);
return buildNativeCRC == prototypeCRC;
} catch (IOException ioe) {
LOG.log(
Level.INFO,
"Cannot read: {0}", //NOI18N
FileUtil.getFileDisplayName(buildNativeXml));
return false;
}
}
static FileObject copyBuildNativeTemplate(@NonNull final Project project) throws IOException {
Parameters.notNull("project", project); //NOI18N
final FileObject buildExFoBack = project.getProjectDirectory().getFileObject(String.format(
"%s~", //NOI18N
EXTENSION_BUILD_SCRIPT_PATH));
if (buildExFoBack != null) {
closeInEditor(buildExFoBack);
buildExFoBack.delete();
}
FileObject buildExFo = project.getProjectDirectory().getFileObject(EXTENSION_BUILD_SCRIPT_PATH);
FileLock lock;
if (buildExFo != null) {
closeInEditor(buildExFo);
lock = buildExFo.lock();
try {
buildExFo.rename(
lock,
buildExFo.getName(),
String.format(
"%s~", //NOI18N
buildExFo.getExt()));
} finally {
lock.releaseLock();
}
}
buildExFo = FileUtil.createData(project.getProjectDirectory(), EXTENSION_BUILD_SCRIPT_PATH);
lock = buildExFo.lock();
try (final InputStream in = J2SEDeployProperties.class.getClassLoader().getResourceAsStream(BUILD_SCRIPT_PROTOTYPE);
final OutputStream out = buildExFo.getOutputStream(lock)) {
FileUtil.copy(in, out);
} finally {
lock.releaseLock();
}
return buildExFo;
}
private static AntBuildExtender.Extension modifyBuildXml(
@NonNull final AntBuildExtender extender,
@NonNull final FileObject buildNativeXml) {
Parameters.notNull("extender", extender); //NOI18N
Parameters.notNull("buildNativeXml", buildNativeXml); //NOI18N
return extender.addExtension(getCurrentExtensionName(), buildNativeXml);
}
private static void closeInEditor(@NonNull final FileObject file) {
Parameters.notNull("file", file); //NOI18N
try {
final DataObject dobj = DataObject.find(file);
final Closable closeCookie = dobj.getLookup().lookup(Closable.class);
if (closeCookie != null) {
closeCookie.close();
}
} catch (DataObjectNotFoundException donfe) {
LOG.log(
Level.INFO,
"Cannot close {0}.", //NOI18N
FileUtil.getFileDisplayName(file));
}
}
private static long computeCRC(@NonNull final InputStream in) throws IOException {
final CRC32 crc = new CRC32();
int last = -1;
int curr;
while ((curr = in.read()) != -1) {
if (curr != '\n' && last == '\r') { //NOI18N
crc.update('\n'); //NOI18N
}
if (curr != '\r') { //NOI18N
crc.update(curr);
}
last = curr;
}
if (last == '\r') { //NOI18N
crc.update('\n'); //NOI18N
}
return crc.getValue();
}
/** Creates a new instance of J2SEDeployProperties */
private J2SEDeployProperties(Lookup context) {
project = context.lookup(Project.class);
if (project != null) {
j2sePropEval = project.getLookup().lookup(J2SEPropertyEvaluator.class);
evaluator = j2sePropEval.evaluator();
nativeBundlingEnabled = isTrue(evaluator.getProperty(NATIVE_BUNDLING_ENABLED));
}
}
private static void setOrRemove(@NonNull org.netbeans.spi.project.support.ant.EditableProperties props, @NonNull String name, String value) {
if (value != null) {
props.setProperty(name, value);
} else {
props.remove(name);
}
}
public void store() throws IOException {
final EditableProperties ep = new EditableProperties(true);
final FileObject projPropsFO = project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
try {
ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try (final InputStream is = projPropsFO.getInputStream()) {
ep.load(is);
}
setOrRemove(ep, NATIVE_BUNDLING_ENABLED, nativeBundlingEnabled ? "true" : null); //NOI18N
FileLock lock = projPropsFO.lock();
try (OutputStream os = projPropsFO.getOutputStream(lock)) {
ep.store(os);
} finally {
lock.releaseLock();
}
final AntBuildExtender extender = project.getLookup().lookup(AntBuildExtender.class);
if (extender != null) {
AntBuildExtender.Extension extension = extender.getExtension(getCurrentExtensionName());
if (nativeBundlingEnabled) {
if (extension == null) {
final FileObject buildExFo = copyBuildNativeTemplate(project);
extension = modifyBuildXml(extender, buildExFo);
}
} else {
if (extension != null) {
extender.removeExtension(getCurrentExtensionName());
}
final FileObject buildExFo = project.getProjectDirectory().getFileObject(EXTENSION_BUILD_SCRIPT_PATH);
if (buildExFo != null) {
closeInEditor(buildExFo);
buildExFo.delete();
}
}
}
return null;
}
});
} catch (MutexException mux) {
throw (IOException) mux.getException();
}
}
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));
}
}