All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.javafx2.project.JFXProjectUtils Maven / Gradle / Ivy

There is a newer version: RELEASE220
Show newest version
/*
 * 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.io.*;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.CRC32;
import java.util.zip.Checksum;

import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.classpath.JavaClassPathConstants;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.api.java.platform.JavaPlatformManager;
import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.java.source.*;
import org.netbeans.api.java.source.ClassIndex.SearchKind;
import org.netbeans.api.java.source.ClassIndex.SearchScope;
import org.netbeans.api.project.*;
import org.netbeans.api.project.ant.AntBuildExtender;
import org.netbeans.api.project.ui.OpenProjects;
import org.netbeans.modules.extbrowser.ExtWebBrowser;
import org.netbeans.modules.java.api.common.project.ProjectProperties;
import org.netbeans.modules.java.j2seproject.api.J2SEProjectPlatform;
import org.netbeans.modules.java.j2seproject.api.J2SEPropertyEvaluator;
import org.netbeans.modules.javafx2.platform.api.JavaFXPlatformUtils;
import org.netbeans.modules.javafx2.platform.api.JavaFxRuntimeInclusion;
import org.netbeans.modules.javafx2.project.JavaFXProjectWizardIterator.WizardType;
import org.netbeans.modules.javafx2.project.fxml.SourceGroupSupport;
import org.netbeans.modules.javafx2.project.fxml.SourceGroupSupport.SourceGroupProxy;
import org.netbeans.modules.javafx2.project.ui.JSEApplicationClassChooser;
import org.netbeans.spi.project.ProjectIconAnnotator;
import org.netbeans.spi.project.support.ant.AntProjectHelper;
import org.netbeans.spi.project.support.ant.EditableProperties;
import org.netbeans.spi.project.support.ant.GeneratedFilesHelper;
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
import org.netbeans.spi.project.support.ant.PropertyUtils;
import org.openide.awt.Notification;
import org.openide.awt.NotificationDisplayer;
import org.openide.cookies.CloseCookie;
import org.openide.execution.NbProcessDescriptor;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.MutexException;
import org.openide.util.NbBundle;
import org.openide.util.Parameters;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Utility class for JavaFX 2.0+ Project
 * 
 * @author Petr Somol
 */
public final class JFXProjectUtils {

    private static Set kinds = new HashSet(Arrays.asList(SearchKind.IMPLEMENTORS));
    private static Set scopes = new HashSet(Arrays.asList(SearchScope.SOURCE));
    
    private static final String JFX_BUILD_TEMPLATE = "Templates/JFX/jfx-impl.xml"; //NOI18N
    private static final String CURRENT_EXTENSION = "jfx3";  //NOI18N
    private static final String[] OLD_EXTENSIONS = new String[] {"jfx", "jfx2"}; // NOI18N
    private static final String NBPROJECT = "nbproject"; // NOI18N
    private static final String JFX_BUILD_IMPL_NAME = "jfx-impl"; // NOI18N
    private static final String JFX_BUILD_IMPL_PATH = NBPROJECT + "/" + JFX_BUILD_IMPL_NAME + ".xml";   //NOI18N
    private static volatile String currentJfxImplCRCCache;
    private static final String TRUE = "true"; //NOI18N
    private static final String FALSE = "false"; //NOI18N
    // from J2SEDeployProperties
    private static final String J2SEDEPLOY_EXTENSION = "j2sedeploy";    //NOI18N
    private static final String EXTENSION_BUILD_SCRIPT_PATH = "nbproject/build-native.xml";        //NOI18N
    //Important path types for Java model
    private static final String[] JAVA_PATHS = {
            ClassPath.BOOT, ClassPath.COMPILE, ClassPath.SOURCE,
            JavaClassPathConstants.MODULE_BOOT_PATH, JavaClassPathConstants.MODULE_COMPILE_PATH,
            JavaClassPathConstants.MODULE_CLASS_PATH, /*JavaClassPathConstants.MODULE_SOURCE_PATH*/
    };

    // two deprecated properties, to be auto-cleaned from project.properties if present
    @Deprecated
    public static final String PROPERTY_JAVAFX_RUNTIME = "javafx.runtime"; // NOI18N
    @Deprecated
    public static final String PROPERTY_JAVAFX_SDK = "javafx.sdk"; // NOI18N

    private static final Logger LOGGER = Logger.getLogger("javafx"); // NOI18N
    
    /**
     * Return current name (version) of build-impl.xml "extension spi"
     * where the spi consists of target dependency points between build-impl
     * and jfx-impl.
     * @return name as string
     */
    public static String getCurrentExtensionName() {
        return CURRENT_EXTENSION;
    }

    /**
     * Return list of old names (versions) of build-impl.xml "extension spis". 
     * Used to search for spi version when jfx-impl and build-impl do not match
     * and correct update needs to be determined.
     * @return names as strings
     */
    public static Iterable getOldExtensionNames() {
        return Arrays.asList(OLD_EXTENSIONS);
    }    

    /**
     * Returns list of JavaFX 2.0 JavaScript callback entries.
     * In future should read the list from the current platform
     * (directly from FX SDK or Ant taks).
     * Current list taken from
     * http://javaweb.us.oracle.com/~in81039/new-dt/js-api/Callbacks.html
     * 
     * @param IDE java platform name
     * @return callback entries
     */
    public static Map/*|null*/> getJSCallbacks(String platformName) {
        final String[][] c = {
            {"onDeployError", "app", "mismatchEvent"}, // NOI18N
            {"onGetNoPluginMessage", "app"}, // NOI18N
            {"onGetSplash", "app"}, // NOI18N
            {"onInstallFinished", "placeholder", "component", "status", "relaunchNeeded"}, // NOI18N
            {"onInstallNeeded", "app", "platform", "cb", "isAutoinstall", "needRelaunch", "launchFunc"}, // NOI18N
            {"onInstallStarted", "placeholder", "component", "isAuto", "restartNeeded"}, // NOI18N
            {"onJavascriptReady", "id"}, // NOI18N
            {"onRuntimeError", "id", "code"} // NOI18N
        };
        Map/*|null*/> m = new LinkedHashMap/*|null*/>();
        for(int i = 0; i < c.length; i++) {
            String[] s = c[i];
            assert s.length > 0;
            List l = null;
            if(s.length > 1) {
                l = new ArrayList();
                for(int j = 1; j < s.length; j++) {
                    l.add(s[j]);
                }
            }
            m.put(s[0], l);
        }
        return m;
    }
    
    /**
     * Returns all classpaths relevant for given project. To be used in
     * main class searches.
     * 
     * @param project
     * @return map of classpaths of all project files
     */
    public static Map> getClassPathMap(@NonNull Project project) {
        Sources sources = ProjectUtils.getSources(project);
        SourceGroup[] srcGroups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
        final Map> classpathMap = new HashMap<>();
        final BitSet requiredPaths = new BitSet(3);
        requiredPaths.set(0, 3, true);
        for (SourceGroup srcGroup : srcGroups) {
            FileObject srcRoot = srcGroup.getRootFolder();
            final Map sgCps = new HashMap<>();
            final BitSet seen = new BitSet(JAVA_PATHS.length);
            for (int i = 0; i < JAVA_PATHS.length; i++) {
                final ClassPath cp = ClassPath.getClassPath(srcRoot, JAVA_PATHS[i]);
                if (cp != null) {
                    sgCps.put(JAVA_PATHS[i], cp);
                    seen.set(i);
                }
            }
            seen.and(requiredPaths);
            if (seen.cardinality() == 3) {
                classpathMap.put(srcRoot, sgCps);
            }
        }
        return classpathMap;
    }

    /**
     * Returns source roots of a project
     * @param project
     * 
     * @return set of fileobjects representing the source roots
     */
    public static Set getSourceRoots(@NonNull Project project) {
        Sources sources = ProjectUtils.getSources(project);
        SourceGroup[] srcGroups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
        final Set sourceRoots = new HashSet();
        for (SourceGroup srcGroup : srcGroups) {
            sourceRoots.add(srcGroup.getRootFolder());
        }
        return sourceRoots;
    }
    
    /**
     * Returns set of names of main classes in the given project
     * 
     * @param project
     * @return set of class names
     */
    public static Set getMainClassNames(@NonNull Project project) {
        final Set mainClassNames = new HashSet();
        FileObject sourceRoots[] = getSourceRoots(project).toArray(new FileObject[0]);
        for (ElementHandle elemHandle : SourceUtils.getMainClasses(sourceRoots)) {
            mainClassNames.add(elemHandle.getQualifiedName());
        }
        return mainClassNames;
    }
    
    /**
     * Returns set of names of classes of the classType type.
     * 
     * @param classpathMap map of classpaths of all project files
     * @param classType return only classes of this type
     * @return set of class names
     */
    public static Set getAppClassNames(@NonNull Collection roots, final @NonNull String classType) {
        final Set appClassNames = new HashSet<>();
        for (FileObject fo : roots) {
            final ClasspathInfo cpInfo = ClasspathInfo.create(fo);
            final JavaSource js = JavaSource.create(cpInfo);
            if (js != null) {
                try { 
                    js.runUserActionTask(new Task() {
                        @Override
                        public void run(CompilationController controller) throws Exception {
                            final ClassIndex classIndex = cpInfo.getClassIndex();
                            final Elements elems = controller.getElements();
                            TypeElement fxAppElement = elems.getTypeElement(classType);
                            ElementHandle appHandle = ElementHandle.create(fxAppElement);
                            Set> appHandles = classIndex.getElements(appHandle, kinds, scopes);
                            for (ElementHandle elemHandle : appHandles) {
                                appClassNames.add(elemHandle.getQualifiedName());
                            }
                        }
                    }, true);
                } catch (Exception e) {

                }
            }
        }
        return appClassNames;
    }


    /** Finds available FX Preloader classes in given JAR files. 
     * Looks for classes specified in the JAR manifest only.
     * 
     * @param jarFile FileObject representing an existing JAR file
     * @param classType return only classes of this type
     * @return set of class names
     */
    public static Set getAppClassNamesInJar(@NonNull FileObject jarFile, final String classType, final String fxrtJarPath) {
        final File jarF = FileUtil.toFile(jarFile);
        if (jarF == null) {
            return null;
        }
        boolean jfxrtExists = false;
        List toLoad = new ArrayList();
        try {
            assert jarF.exists();
            toLoad.add(jarF.toURI().toURL());
            final File jfxrt = new File(fxrtJarPath);
            if(jfxrt.exists()) {
                jfxrtExists = true;
            }
            toLoad.add(jfxrt.toURI().toURL());            

        } catch (MalformedURLException ex) {
            return null;
        }        
        URLClassLoader clazzLoader = URLClassLoader.newInstance(toLoad.toArray(new URL[0]));

        final Set appClassNames = new HashSet();
        JarFile jf;
        try {
            jf = new JarFile(jarF);
        } catch (IOException x) {
            return null;
        }
        Enumeration entries = jf.entries();
        if (entries == null) {
            return null;
        }        
        while(entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            // Relative path of file into the jar
            String classFileName = entry.getName();
            
            if(!classFileName.endsWith(".class")) { // NOI18N
                continue;
            }
            if(classFileName.contains("$")) { // NOI18N
                continue;
            }
            // Complete class name
            String className = classFileName.replace(".class", "").replace('\\', '/').replace('/', '.'); // NOI18N
            
            if(clazzLoader != null && jfxrtExists) {
                // Load class definition from JVM
                Class clazz;
                try {
                    clazz = Class.forName(className, true, clazzLoader);
                    Type t = clazz.getGenericSuperclass();
                    if(t.toString().contains(classType)) {
                        if (className.startsWith(".")) { // NOI18N
                            className = className.substring(1);
                        }
                        appClassNames.add(className);
                    }
                } catch (ClassNotFoundException ex) {
                    return null;
                }
            } else {
                if (className.startsWith(".")) { // NOI18N
                    className = className.substring(1);
                }
                appClassNames.add(className);
            }
        }

        return appClassNames;
    }

    /**
     * Checks if the JFX support is enabled for given project
     * @param prj the project to check
     * @return true if project supports JFX
     */
    public static boolean isFXProject(@NonNull final Project prj) {
        final J2SEPropertyEvaluator ep = prj.getLookup().lookup(J2SEPropertyEvaluator.class);
        if (ep == null) {
            return false;
        }
        return JFXProjectProperties.isTrue(ep.evaluator().getProperty(JFXProjectProperties.JAVAFX_ENABLED));
    }

     /**
     * Checks if the project is a Swing project with JFX support enabled
     * @param prj the project to check
     * @return true if project supports FX in Swing
     */
    public static boolean isFXinSwingProject(@NonNull final Project prj) {
        final J2SEPropertyEvaluator ep = prj.getLookup().lookup(J2SEPropertyEvaluator.class);
        if (ep == null) {
            return false;
        }
        return JFXProjectProperties.isTrue(ep.evaluator().getProperty(JFXProjectProperties.JAVAFX_ENABLED))
                && JFXProjectProperties.isTrue(ep.evaluator().getProperty(JFXProjectProperties.JAVAFX_SWING));
    }

     /**
     * Checks if the project is a JavaFX preloader project. Note that in pre-7.2 NB
     * preloader type had been used for fx-in-swing as workaround, hence the logic below
     * @param prj the project to check
     * @return true if project is JavaFX preloader
     */
    public static boolean isFXPreloaderProject(@NonNull final Project prj) {
        final J2SEPropertyEvaluator ep = prj.getLookup().lookup(J2SEPropertyEvaluator.class);
        if (ep == null) {
            return false;
        }
        return JFXProjectProperties.isTrue(ep.evaluator().getProperty(JFXProjectProperties.JAVAFX_ENABLED))
                && JFXProjectProperties.isTrue(ep.evaluator().getProperty(JFXProjectProperties.JAVAFX_PRELOADER))
                && !JFXProjectProperties.isTrue(ep.evaluator().getProperty(JFXProjectProperties.JAVAFX_SWING));
    }

    /**
     * Checks if the project uses Maven build system
     *
     * @param prj the project to check
     * @return true if is Maven project
     */
    public static boolean isMavenProject(@NonNull final Project prj) {
        FileObject fo = prj.getProjectDirectory();
        if (fo == null || !fo.isValid()) {
            return false;
        }
        fo = fo.getFileObject("pom.xml"); //NOI18N
        return fo != null;
    }

    /**
     * Checks if the project uses Gradle build system
     *
     * @param prj the project to check
     * @return true if is Gradle project
     */
    public static boolean isGradleProject(@NonNull final Project prj) {
        FileObject fo = prj.getProjectDirectory();
        if (fo == null || !fo.isValid()) {
            return false;
        }
        fo = fo.getFileObject("build.gradle"); //NOI18N
        return fo != null;
    }

    public static boolean isMavenFXProject(@NonNull final Project prj) {
        if (isMavenProject(prj)) {
            try {
                FileObject pomXml = prj.getProjectDirectory().getFileObject("pom.xml"); //NOI18N
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document doc = builder.parse(FileUtil.toFile(pomXml));
                XPathFactory xPathfactory = XPathFactory.newInstance();
                XPath xpath = xPathfactory.newXPath();
                XPathExpression exprJfxrt = xpath.compile("//bootclasspath[contains(text(),'jfxrt')]"); //NOI18N
                XPathExpression exprFxPackager = xpath.compile("//executable[contains(text(),'javafxpackager')]"); //NOI18N
                XPathExpression exprPackager = xpath.compile("//executable[contains(text(),'javapackager')]"); //NOI18N
                boolean jfxrt = (Boolean) exprJfxrt.evaluate(doc, XPathConstants.BOOLEAN);
                boolean packager = (Boolean) exprPackager.evaluate(doc, XPathConstants.BOOLEAN);
                boolean fxPackager = (Boolean) exprFxPackager.evaluate(doc, XPathConstants.BOOLEAN);
                return jfxrt && (packager || fxPackager);
            } catch (XPathExpressionException | ParserConfigurationException | SAXException | IOException ex) {
                LOGGER.log(Level.INFO, "Error while parsing pom.xml.", ex);  //NOI18N
                return false;
            }
        }
        return false;
    }

    /**
     * Checks what Run model is selected in current configuration of JFX Run Project Property panel
     * @param prj the project to check
     * @return string value of JFXProjectProperties.RunAsType type or null meaning JFXProjectProperties.RunAsType.STANDALONE
     */
    public static String getFXProjectRunAs(@NonNull final Project prj) {
        final J2SEPropertyEvaluator ep = prj.getLookup().lookup(J2SEPropertyEvaluator.class);
        if (ep == null) {
            return null;
        }
        return ep.evaluator().getProperty(JFXProjectProperties.RUN_AS);
    }

    /**
     * Finds the relative path to targetFO from sourceFO. 
     * Unlike FileUtil.getRelativePath() does not require targetFO to be within sourceFO sub-tree
     * Returns null if there is no shared parent directory except root.
     * 
     * @param sourceFO file/dir to which the relative path will be related
     * @param targetFO file whose location will be determined with respect to sourceFO
     * @return string relative path leading from sourceFO to targetFO
     */
    public static String getRelativePath(@NonNull final FileObject sourceFO, @NonNull final FileObject targetFO) {
        String path = ""; //NOI18N
        FileObject src = sourceFO;
        FileObject tgt = targetFO;
        String targetName = null;
        if(!src.isFolder()) {
            src = src.getParent();
        }
        if(!tgt.isFolder()) {
            targetName = tgt.getNameExt();
            tgt = tgt.getParent();
        }
        LinkedList srcSplit = new LinkedList();
        LinkedList tgtSplit = new LinkedList();
        while(!src.isRoot()) {
            srcSplit.addFirst(src.getName());
            src = src.getParent();
        }
        while(!tgt.isRoot()) {
            tgtSplit.addFirst(tgt.getName());
            tgt = tgt.getParent();
        }
        boolean share = false;
        while(!srcSplit.isEmpty() && !tgtSplit.isEmpty()) {
            if(srcSplit.getFirst().equals(tgtSplit.getFirst())) {
                srcSplit.removeFirst();
                tgtSplit.removeFirst();
                share = true;
            } else {
                break;
            }
        }
        if(!share) {
            return null;
        }
        for(int left = 0; left < srcSplit.size(); left++) {
            if(left == 0) {
                path += ".."; //NOI18N
            } else {
                path += "/.."; //NOI18N
            }
        }
        while(!tgtSplit.isEmpty()) {
            if(path.isEmpty()) {
                path += tgtSplit.getFirst();
            } else {
                path += "/" + tgtSplit.getFirst(); //NOI18N
            }
            tgtSplit.removeFirst();
        }
        if(targetName != null) {
            if(!path.isEmpty()) {
                path += "/" + targetName; //NOI18N
            } else {
                path += targetName;
            }
        }
        return path;
    }

    /**
     * Finds the file/dir represented by relPath with respect to sourceDir. 
     * Returns null if the file does not exist.
     * 
     * @param sourceDir file/dir to which the relative path is related
     * @param relPath relative path related to sourceDir
     * @return FileObject or null
     */
    public static FileObject getFileObject(@NonNull final FileObject sourceDir, @NonNull final String relPath) {
        String split[] = relPath.split("[\\\\/]+"); //NOI18N
        FileObject src = sourceDir;
        String path = ""; //NOI18N
        boolean back = true;
        if(split[0].equals("..")) {
            for(int i = 0; i < split.length; i++) {
                if(back && split[i].equals("..")) { //NOI18N
                    src = src.getParent();
                    if(src == null) {
                        return null;
                    }
                } else {
                    if(back) {
                        back = false;
                        path = src.getPath();
                    }
                    path += "/" + split[i]; //NOI18N
                }
            }
        } else {
            path = relPath;
        }
        File f = new File(path);
        if(f.exists()) {
            return FileUtil.toFileObject(f);
        }
        return null;
    }

    /**
     * Initialize project properties for JavaFX Application project type
     * @param ep
     * @param type
     * @param platformName
     * @param mainClass
     * @param preloader
     * @throws MissingResourceException 
     */
    static void initializeJavaFXProperties(@NonNull EditableProperties ep, @NonNull WizardType type, String targetPlatformName, @NonNull String mainClass, String preloader) throws MissingResourceException {
        ep.setProperty(JFXProjectProperties.JAVAFX_ENABLED, TRUE); // NOI18N
        ep.setComment(JFXProjectProperties.JAVAFX_ENABLED, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_javafx")}, false); // NOI18N
        ep.setProperty("jnlp.enabled", FALSE); // NOI18N
        ep.setComment("jnlp.enabled", new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_oldjnlp")}, false); // NOI18N
        ep.setProperty(ProjectProperties.COMPILE_ON_SAVE, TRUE); // NOI18N
        ep.setProperty(ProjectProperties.COMPILE_ON_SAVE_UNSUPPORTED_PREFIX + ".javafx", TRUE); // NOI18N
        ep.setProperty(JFXProjectProperties.JAVAFX_BINARY_ENCODE_CSS, FALSE); // NOI18N
        ep.setProperty(JFXProjectProperties.JAVAFX_DEPLOY_INCLUDEDT, TRUE); // NOI18N
        ep.setProperty(JFXProjectProperties.JAVAFX_DEPLOY_EMBEDJNLP, TRUE); // NOI18N
        ep.setProperty(JFXProjectProperties.JAVAFX_REBASE_LIBS, FALSE); // NOI18N
        ep.setComment(JFXProjectProperties.JAVAFX_REBASE_LIBS, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_rebase_libs")}, false); // NOI18N
        ep.setProperty(JFXProjectProperties.JAVAFX_DISABLE_CONCURRENT_RUNS, FALSE); // NOI18N
        ep.setComment(JFXProjectProperties.JAVAFX_DISABLE_CONCURRENT_RUNS, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_disable_concurrent_runs")}, false); // NOI18N
        ep.setProperty(JFXProjectProperties.JAVAFX_ENABLE_CONCURRENT_EXTERNAL_RUNS, FALSE); // NOI18N
        ep.setComment(JFXProjectProperties.JAVAFX_ENABLE_CONCURRENT_EXTERNAL_RUNS, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_enable_concurrent_external_runs")}, false); // NOI18N
        ep.setProperty(JFXProjectProperties.UPDATE_MODE_BACKGROUND, FALSE); // NOI18N
        ep.setComment(JFXProjectProperties.UPDATE_MODE_BACKGROUND, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, type == WizardType.SWING ? "COMMENT_updatemode_swing" : "COMMENT_updatemode")}, false); // NOI18N
        ep.setProperty(JFXProjectProperties.ALLOW_OFFLINE, TRUE); // NOI18N
        if(targetPlatformName == null) {
            targetPlatformName = ep.getProperty(JFXProjectProperties.PLATFORM_ACTIVE);
        }
        Collection extensions = JavaFxRuntimeInclusion.getProjectClassPathExtension(JavaFXPlatformUtils.findJavaPlatform(targetPlatformName));
        if (extensions != null && !extensions.isEmpty()) {
            ep.setProperty(JavaFXPlatformUtils.JAVAFX_CLASSPATH_EXTENSION, JFXProjectUtils.getPaths(extensions));
            ep.setProperty(ProjectProperties.JAVAC_CLASSPATH, new String[]{JavaFXPlatformUtils.getClassPathExtensionProperty()});
        }
        ep.setProperty(ProjectProperties.ENDORSED_CLASSPATH, ""); // NOI18N
        ep.setProperty(JFXProjectProperties.RUN_APP_WIDTH, "800"); // NOI18N
        ep.setProperty(JFXProjectProperties.RUN_APP_HEIGHT, "600"); // NOI18N
        if (type == WizardType.PRELOADER) {
            ep.setProperty(JFXProjectProperties.JAVAFX_PRELOADER, TRUE); // NOI18N
            ep.setComment(JFXProjectProperties.JAVAFX_PRELOADER, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_preloader")}, false); // NOI18N
            ep.setProperty(JFXProjectProperties.PRELOADER_ENABLED, FALSE); // NOI18N
            ep.setComment(JFXProjectProperties.PRELOADER_ENABLED, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_prepreloader")}, false); // NOI18N
        } else {
            ep.setProperty("jar.archive.disabled", TRUE); // NOI18N
            ep.setComment("jar.archive.disabled", new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_oldjar")}, false); // NOI18N
            ep.setProperty(ProjectProperties.MAIN_CLASS, type == WizardType.SWING ? (mainClass == null ? "" : mainClass) : "com.javafx.main.Main"); // NOI18N
            ep.setComment(ProjectProperties.MAIN_CLASS, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_main.class")}, false); // NOI18N
            if (type != WizardType.LIBRARY) {
                ep.setProperty(JFXProjectProperties.MAIN_CLASS, mainClass == null ? "" : mainClass); // NOI18N
                ep.setComment(JFXProjectProperties.MAIN_CLASS, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_main.fxclass")}, false); // NOI18N
            }
            if (preloader != null && preloader.length() > 0) {
                String preloaderProjRelative = "../" + preloader; // NOI18N
                String preloaderJarFileName = preloader + ".jar"; // NOI18N
                String copiedPreloaderJarPath = "${dist.dir}/lib/${" + JFXProjectProperties.PRELOADER_JAR_FILENAME + "}"; // NOI18N
                ep.setProperty(JFXProjectProperties.PRELOADER_ENABLED, TRUE); // NOI18N
                ep.setProperty(JFXProjectProperties.PRELOADER_TYPE, JFXProjectProperties.PreloaderSourceType.PROJECT.getString());
                ep.setComment(JFXProjectProperties.PRELOADER_ENABLED, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_use_preloader")}, false); // NOI18N
                ep.setProperty(JFXProjectProperties.PRELOADER_PROJECT, preloaderProjRelative);
                ep.setProperty(JFXProjectProperties.PRELOADER_CLASS, JavaFXProjectWizardIterator.generatePreloaderClassName(preloader));
                ep.setProperty(JFXProjectProperties.PRELOADER_JAR_PATH, copiedPreloaderJarPath);
                ep.setProperty(JFXProjectProperties.PRELOADER_JAR_FILENAME, preloaderJarFileName);
            } else {
                ep.setProperty(JFXProjectProperties.PRELOADER_ENABLED, FALSE); // NOI18N
                ep.setProperty(JFXProjectProperties.PRELOADER_TYPE, JFXProjectProperties.PreloaderSourceType.NONE.getString());
                ep.setComment(JFXProjectProperties.PRELOADER_ENABLED, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_dontuse_preloader")}, false); // NOI18N
                ep.setProperty(JFXProjectProperties.PRELOADER_PROJECT, ""); // NOI18N
                ep.setProperty(JFXProjectProperties.PRELOADER_CLASS, ""); // NOI18N
                ep.setProperty(JFXProjectProperties.PRELOADER_JAR_PATH, ""); // NOI18N
                ep.setProperty(JFXProjectProperties.PRELOADER_JAR_FILENAME, ""); // NOI18N
            }
            if (type == WizardType.SWING) {
                ep.setProperty(JFXProjectProperties.JAVAFX_SWING, TRUE); // NOI18N
                ep.setComment(JFXProjectProperties.JAVAFX_SWING, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_use_swing")}, false); // NOI18N
            }
        }
        ep.setProperty(JFXProjectProperties.IMPLEMENTATION_VERSION, JFXProjectProperties.IMPLEMENTATION_VERSION_DEFAULT);
        ep.setProperty(JFXProjectProperties.FALLBACK_CLASS, "com.javafx.main.NoJavaFXFallback"); // NOI18N
        // extended manifest entries
        ep.setProperty(JFXProjectProperties.MANIFEST_CUSTOM_CODEBASE, "*"); // NOI18N
        ep.setComment(JFXProjectProperties.MANIFEST_CUSTOM_CODEBASE, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_manifest_custom_codebase")}, false); // NOI18N
        ep.setProperty(JFXProjectProperties.MANIFEST_CUSTOM_PERMISSIONS, ""); // NOI18N
        ep.setComment(JFXProjectProperties.MANIFEST_CUSTOM_PERMISSIONS, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_manifest_custom_permissions")}, false); // NOI18N
        // SE->FX conversion cleanup
        ep.remove(JFXProjectProperties.JAVASE_KEEP_JFXRT_ON_CLASSPATH);
    }

    /**
     * Modify project.properties file to contain all javafx relevant properties with initial values
     * @param project
     * @throws IOException 
     */
    public static void resetJavaFXProjectProperties(@NonNull final Project project, @NonNull final WizardType type, final String targetPlatformName, @NonNull final String mainClass, final String preloader) 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() {
                @Override
                public Void run() throws Exception {
                    final Lookup lookup = project.getLookup();
                    final J2SEPropertyEvaluator eval = lookup.lookup(J2SEPropertyEvaluator.class);
                    String platformNameToCheck = null;
                    if (eval != null) {
                        String currentPlatformName = eval.evaluator().getProperty(JFXProjectProperties.PLATFORM_ACTIVE);
                        platformNameToCheck = currentPlatformName;
                        JavaPlatform platform = null;
                        if(targetPlatformName == null) {
                            platform = JavaFXPlatformUtils.findJavaPlatform(currentPlatformName);
                        } else {
                            platform = JavaFXPlatformUtils.findJavaPlatform(targetPlatformName);
                        }
                        if(platform == null || !JavaFXPlatformUtils.isJavaFXEnabled(platform)) {
                            platform = JavaFXPlatformUtils.findJavaFXPlatform();
                        }
                        if(platform != null && !JFXProjectProperties.isEqual(currentPlatformName, JavaFXPlatformUtils.getPlatformAntName(platform))) {
                            final J2SEProjectPlatform platformSetter = lookup.lookup(J2SEProjectPlatform.class);
                            if(platformSetter != null) {
                                platformSetter.setProjectPlatform(platform);
                                platformNameToCheck = JavaFXPlatformUtils.getPlatformAntName(platform);
                            }
                        }
                    }
                    final InputStream is = projPropsFO.getInputStream();
                    try {
                        ep.load(is);
                    } finally {
                        if (is != null) {
                            is.close();
                        }
                    }
                    initializeJavaFXProperties(ep, type, platformNameToCheck, mainClass, preloader);
                    OutputStream os = null;
                    FileLock lock = null;
                    try {
                        lock = projPropsFO.lock();
                        os = projPropsFO.getOutputStream(lock);
                        ep.store(os);
                    } finally {
                        if (os != null) {
                            os.close();
                        }
                        if (lock != null) {
                            lock.releaseLock();
                        }
                    }
                    return null;
                }
            });
        } catch (MutexException mux) {
            throw (IOException) mux.getException();
        }
    }

    /**
     * Adds FX specific build script jfx-impl.xml to project build system
     * @param p
     * @param dirFO
     * @param type
     * @throws IOException 
     */
    static void createJfxExtension(Project p, FileObject dirFO, WizardType type) throws IOException {
        FileObject templateFO = FileUtil.getConfigFile("Templates/JFX/jfx-impl.xml"); // NOI18N
        if (templateFO != null) {
            FileObject nbprojectFO = dirFO.getFileObject("nbproject"); // NOI18N
            FileObject jfxBuildFile = FileUtil.copyFile(templateFO, nbprojectFO, "jfx-impl"); // NOI18N
            if (type == JavaFXProjectWizardIterator.WizardType.SWING) {
                FileObject templatesFO = nbprojectFO.getFileObject("templates"); // NOI18N
                if (templatesFO == null) {
                    templatesFO = nbprojectFO.createFolder("templates"); // NOI18N
                }
                FileObject swingTemplateFO1 = FileUtil.getConfigFile("Templates/JFX/FXSwingTemplate.html"); // NOI18N
                if (swingTemplateFO1 != null) {
                    FileUtil.copyFile(swingTemplateFO1, templatesFO, "FXSwingTemplate"); // NOI18N
                }
                FileObject swingTemplateFO2 = FileUtil.getConfigFile("Templates/JFX/FXSwingTemplateApplet.jnlp"); // NOI18N
                if (swingTemplateFO1 != null) {
                    FileUtil.copyFile(swingTemplateFO2, templatesFO, "FXSwingTemplateApplet"); // NOI18N
                }
                FileObject swingTemplateFO3 = FileUtil.getConfigFile("Templates/JFX/FXSwingTemplateApplication.jnlp"); // NOI18N
                if (swingTemplateFO1 != null) {
                    FileUtil.copyFile(swingTemplateFO3, templatesFO, "FXSwingTemplateApplication"); // NOI18N
                }
            }
            JFXProjectUtils.addExtension(p);
        }
    }

    /**
     * Adds dependencies of build-impl targets on jfx-impl targets
     * @param proj
     * @return true if success
     * @throws IOException 
     */
    static boolean addExtension(@NonNull Project proj) throws IOException {
        boolean res = false;
        FileObject projDir = proj.getProjectDirectory();
        FileObject jfxBuildFile = projDir.getFileObject(JFX_BUILD_IMPL_PATH);
        AntBuildExtender extender = proj.getLookup().lookup(AntBuildExtender.class);
        if (extender != null) {
            assert jfxBuildFile != null;
            if (extender.getExtension(CURRENT_EXTENSION) == null) { // NOI18N                
                AntBuildExtender.Extension ext = extender.addExtension(CURRENT_EXTENSION, jfxBuildFile); // NOI18N
                // NOTE: change in dependencies = change of metafile updates API;
                //       do not forget to update CURRENT_EXTENSION and add the old one to OLD_EXTENSIONS
                ext.addDependency("-post-jar", "-jfx-copylibs"); // NOI18N
                ext.addDependency("-post-jar", "-rebase-libs"); //NOI18N
                ext.addDependency("-post-jar", "jfx-deployment"); //NOI18N 
                ext.addDependency("run", "jar"); //NOI18N
                ext.addDependency("debug", "jar");//NOI18N
                ext.addDependency("profile", "jar");//NOI18N
                res = true;
            }
        }
        return res;
    }

    /**
     * Remove SE native packaging extension and build script
     * @param project 
     */
    static void removeSENativeBundlerExtension(@NonNull Project project) throws IOException {
        final AntBuildExtender extender = project.getLookup().lookup(AntBuildExtender.class);
        if (extender != null) {
            AntBuildExtender.Extension extension = extender.getExtension(J2SEDEPLOY_EXTENSION);
            if (extension != null) {
                extender.removeExtension(J2SEDEPLOY_EXTENSION);
            }
            final FileObject buildExFo = project.getProjectDirectory().getFileObject(EXTENSION_BUILD_SCRIPT_PATH);
            if (buildExFo != null) {
                buildExFo.delete();
            }
        }
    }
    
    /**
     * Modify Java Application (SE) project to become JavaFX Application, i.e.,
     * set Application class, create FX build scripts, modify properties,
     * and turn on FX mode by setting property javafx.enabled=true;
     * @param project
     * @throws IOException 
     */
    public static void switchProjectToFX(@NonNull final Project project, @NonNull final JSEApplicationClassChooser chooser) throws IOException {
        final FileObject dirFO = project.getProjectDirectory();
        dirFO.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() {
            @Override
            public void run() throws IOException {
                removeSENativeBundlerExtension(project);
                createJfxExtension(project, dirFO, WizardType.APPLICATION);
                ProjectManager.getDefault().saveProject(project);
                updateJFXExtension(project);
                
                //set main class
                String appClass = chooser.getSelectedExistingClass();
                if(appClass == null) {
                    FileObject dirController = chooser.getCurrentPackageFolder(true);
                    DataFolder packageDataFolder = DataFolder.findFolder(dirController);
                    String newClassName = chooser.getCurrentFileName();
                    if (packageDataFolder != null) {
                        FileObject javaTemplate = FileUtil.getConfigFile("Templates/javafx/FXMain.java"); // NOI18N
                        DataObject dJavaTemplate = DataObject.find(javaTemplate);
                        DataObject dobj = dJavaTemplate.createFromTemplate(packageDataFolder, newClassName);
                    }
                    appClass = chooser.getPackageName() + "." + newClassName; // NOI18N
                }
                resetJavaFXProjectProperties(project, WizardType.APPLICATION, null, appClass, null); // NOI18N
                
                for (ProjectIconAnnotator annotator : Lookup.getDefault().lookupAll(ProjectIconAnnotator.class)) {
                    if(annotator instanceof JFXProjectIconAnnotator) {
                        JFXProjectIconAnnotator fxAnnotator = (JFXProjectIconAnnotator) annotator;
                        fxAnnotator.fireChange(project, true);
                    }
                }
            }
        });
        final String headerTemplate = NbBundle.getMessage(JFXProjectUtils.class, "TXT_SWITCHED_SE_TO_FX_HEADER"); //NOI18N
        final String header = MessageFormat.format(headerTemplate, new Object[] {ProjectUtils.getInformation(project).getDisplayName()});
        final String content = NbBundle.getMessage(JFXProjectUtils.class, "TXT_SWITCHED_SE_TO_FX_CONTENT"); //NOI18N
        Notification notePlatformChange = NotificationDisplayer.getDefault().notify(
                header, 
                ImageUtilities.loadImageIcon("org/netbeans/modules/javafx2/project/ui/resources/jfx_project.png", true), //NOI18N
                content, 
                null, 
                NotificationDisplayer.Priority.LOW, 
                NotificationDisplayer.Category.INFO);
        JFXProjectOpenedHook.addNotification(project, notePlatformChange);
    }
    
    /**
     * Update dependencies of build-impl targets on jfx-impl targets
     * @param project
     * @return
     * @throws IOException 
     */
    public static boolean updateJFXExtension(final Project project) throws IOException {
        boolean changed = modifyBuildXml(project);
        return changed;
    }

    /**
     * Update SE buildscript dependencies on FX buildscript
     * @param proj
     * @return
     * @throws IOException 
     */
    private static boolean modifyBuildXml(Project proj) throws IOException {
        boolean res = false;
        final FileObject buildXmlFO = getBuildXml(proj);
        if (buildXmlFO == null) {
            LOGGER.warning("The project build script does not exist, the project cannot be extended by JFX.");     //NOI18N
            return res;
        }
        Document xmlDoc = null;
        try {
            xmlDoc = XMLUtil.parse(new InputSource(buildXmlFO.toURL().toExternalForm()), false, true, null, null);
        } catch (SAXException ex) {
            Exceptions.printStackTrace(ex);
        }
        if(!addExtension(proj)) {
            LOGGER.log(Level.INFO,
                    "Trying to include JFX build snippet in project type that doesn't support AntBuildExtender API contract."); // NOI18N
        }

        //TODO this piece shall not proceed when the upgrade to j2se-project/4 was cancelled.
        //how to figure..
        Element docElem = xmlDoc.getDocumentElement();
        NodeList nl = docElem.getElementsByTagName("target"); // NOI18N
        boolean changed = false;
        nl = docElem.getElementsByTagName("import"); // NOI18N
        for (int i = 0; i < nl.getLength(); i++) {
            Element e = (Element) nl.item(i);
            if (e.getAttribute("file") != null && JFX_BUILD_IMPL_PATH.equals(e.getAttribute("file"))) { // NOI18N
                e.getParentNode().removeChild(e);
                changed = true;
                break;
            }
        }

        if (changed) {
            final Document fdoc = xmlDoc;
            try {
                ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() {
                    @Override
                    public Void run() throws Exception {
                        FileLock lock = buildXmlFO.lock();
                        try {
                            OutputStream os = buildXmlFO.getOutputStream(lock);
                            try {
                                XMLUtil.write(fdoc, os, "UTF-8"); // NOI18N
                            } finally {
                                os.close();
                            }
                        } finally {
                            lock.releaseLock();
                        }
                        return null;
                    }
                });
            } catch (MutexException mex) {
                throw (IOException) mex.getException();
            }
        }
        return res;
    }

    /**
     * Get build.xml
     * @param prj
     * @return 
     */
    private static FileObject getBuildXml(final Project prj) {
        final J2SEPropertyEvaluator j2sepe = prj.getLookup().lookup(J2SEPropertyEvaluator.class);
        assert j2sepe != null;
        final PropertyEvaluator eval = j2sepe.evaluator();
        String buildScriptPath = eval.getProperty(JFXProjectProperties.BUILD_SCRIPT);
        if (buildScriptPath == null) {
            buildScriptPath = GeneratedFilesHelper.BUILD_XML_PATH;
        }
        return prj.getProjectDirectory().getFileObject (buildScriptPath);
    }

    /**
     * Update FX build script.
     * @param proj
     * @return
     * @throws IOException 
     */
    public static FileObject updateJfxImpl(final @NonNull Project proj) throws IOException {
        final FileObject projDir = proj.getProjectDirectory();
        final List updates = new ArrayList();
        try {
            ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() {
                @Override
                public Void run() throws Exception {        
                    projDir.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() {
                        public void run() throws IOException {        
                            if(!JFXProjectUtils.isJFXImplCurrent(proj)) {
                                FileObject updated = JFXProjectUtils.doUpdateJfxImpl(proj);
                                if(updated != null) {
                                    updates.add(updated);
                                }
                            }
                        }
                    });
                    return null;
                }
            });
        } catch (MutexException ex) {
            Exceptions.printStackTrace(ex);
        }
        return updates.isEmpty() ? null : updates.get(0);
    }
    
    /**
     * Checks whether file nbproject/jfx-impl.xml equals current template. 
     * @param proj
     * @return
     * @throws IOException 
     */
    public static boolean isJFXImplCurrent(final @NonNull Project proj) throws IOException {
        Boolean isJfxCurrent = true;
        final FileObject projDir = proj.getProjectDirectory();
        try {
            isJfxCurrent = ProjectManager.mutex().readAccess(new Mutex.ExceptionAction() {
                @Override
                public Boolean run() throws Exception {
                    FileObject jfxBuildFile = projDir.getFileObject(JFX_BUILD_IMPL_PATH); // NOI18N
                    Boolean isCurrent = false;
                    if (jfxBuildFile != null) {
                        final InputStream in = jfxBuildFile.getInputStream();
                        if(in != null) {
                            try {
                                isCurrent = isJfxImplCurrentVer(computeCrc32( in ));
                            } finally {
                                in.close();
                            }
                        }
                    }
                    return isCurrent;
                }
            });
        } catch (MutexException mux) {
            isJfxCurrent = false;
            LOGGER.log(Level.INFO, "Problem reading " + JFX_BUILD_IMPL_PATH, mux.getException()); // NOI18N
        }
        return isJfxCurrent;
    }    

    /**
     * The file nbproject/jfx-impl.xml is backed up and regenerated to the current state
     * and textual commentary is generated to UPDATED.TXT.
     * 
     * @param prj the project to check
     * @return FileObject pointing at generated UPDATED.TXT or null
     */
    static FileObject doUpdateJfxImpl(final @NonNull Project proj) throws IOException {
        FileObject returnFO = null;
        final FileObject projDir = proj.getProjectDirectory();
        try {
            returnFO = ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() {
                @Override
                public FileObject run() throws Exception {
                    FileObject returnFO = null;
                    FileObject jfxBuildFile = projDir.getFileObject(JFX_BUILD_IMPL_PATH); // NOI18N
                    if (jfxBuildFile != null) {
                        // try to close the file just in case the file is already opened in editor
                        DataObject dobj = DataObject.find(jfxBuildFile);
                        CloseCookie closeCookie = dobj.getLookup().lookup(CloseCookie.class);
                        if (closeCookie != null) {
                            closeCookie.close();
                        }
                        closeCookie = null;
                        dobj = null;

                        final FileObject nbproject = projDir.getFileObject(NBPROJECT); //NOI18N
                        final String backupName = FileUtil.findFreeFileName(nbproject, JFX_BUILD_IMPL_NAME + "_backup", "xml"); //NOI18N
                        FileUtil.moveFile(jfxBuildFile, nbproject, backupName);
                        LOGGER.log(Level.INFO, "Old build script file " + JFX_BUILD_IMPL_NAME + ".xml has been renamed to: {0}.xml", backupName); // NOI18N
                        jfxBuildFile = null;

                        try {
                            final File readme = new File (FileUtil.toFile(nbproject), NbBundle.getMessage(JFXProjectUtils.class, "TXT_UPDATED_README_FILE_NAME")); //NOI18N
                            if (!readme.exists()) {
                                readme.createNewFile();
                            }
                            final FileObject readmeFO = FileUtil.toFileObject(readme);
                            returnFO = readmeFO;
                            OutputStream os = null;
                            FileLock lock = null;
                            try {
                                lock = readmeFO.lock();
                                os = readmeFO.getOutputStream(lock);
                                final PrintWriter out = new PrintWriter ( os );
                                try {
                                    final String headerTemplate = NbBundle.getMessage(JFXProjectUtils.class, "TXT_UPDATED_README_FILE_CONTENT_HEADER"); //NOI18N
                                    final String header = MessageFormat.format(headerTemplate, new Object[] {ProjectUtils.getInformation(proj).getDisplayName()});
                                    char[] underline = new char[header.length()];
                                    Arrays.fill(underline, '='); // NOI18N
                                    final String content = NbBundle.getMessage(JFXProjectUtils.class, "TXT_UPDATED_README_FILE_CONTENT"); //NOI18N
                                    out.println(underline);
                                    out.println(header);
                                    out.println(underline);
                                    out.println (MessageFormat.format(content, new Object[] {backupName + ".xml"})); //NOI18N
                                } finally {
                                    if(out != null) {
                                        out.close ();
                                    }
                                }
                            } finally {
                                if (os != null) {
                                    os.close();
                                }
                                if (lock != null) {
                                    lock.releaseLock();
                                }
                            }
                        } catch (IOException ioe) {
                            LOGGER.log(Level.INFO, "Cannot create file readme file. ", ioe); // NOI18N
                        }        
                    }
                    if (jfxBuildFile == null) {
                        FileObject templateFO = FileUtil.getConfigFile(JFX_BUILD_TEMPLATE);
                        if (templateFO != null) {
                            FileUtil.copyFile(templateFO, projDir.getFileObject(NBPROJECT), JFX_BUILD_IMPL_NAME); // NOI18N
                            LOGGER.log(Level.INFO, "Build script " + JFX_BUILD_IMPL_NAME + ".xml has been updated to the latest version supported by this NetBeans installation."); // NOI18N
                        } 
                    }
                    return returnFO;
                } //run()
            });
        } catch(MutexException mux) {
            throw (IOException) mux.getException();
        }
        return returnFO;
    }

    /**
     * Computes CRC code of data from InputStream
     * 
     * @param is InputStream to read data from
     * @return CRC code
     */
    public static String computeCrc32(InputStream is) throws IOException {
        Checksum crc = new CRC32();
        int last = -1;
        int curr;
        while ((curr = is.read()) != -1) {
            if (curr != '\n' && last == '\r') {
                crc.update('\n');
            }
            if (curr != '\r') {
                crc.update(curr);
            }
            last = curr;
        }
        if (last == '\r') {
            crc.update('\n');
        }
        int val = (int)crc.getValue();
        String hex = Integer.toHexString(val);
        while (hex.length() < 8) {
            hex = "0" + hex; // NOI18N
        }
        return hex;
    }

    /**
     * Checks whether crc is the CRC code of current jfx-impl.xml template.
     * 
     * @param crc code to be compared against
     * @return true if crc is the CRC code of current jfx-impl.xml template.
     */
    public static boolean isJfxImplCurrentVer(String crc) throws IOException {
        String _currentJfxImplCRC = currentJfxImplCRCCache;
        if (_currentJfxImplCRC == null) {
            final FileObject template = FileUtil.getConfigFile(JFX_BUILD_TEMPLATE);
            final InputStream in = template.getInputStream();
            if(in != null) {
                try {
                    currentJfxImplCRCCache = _currentJfxImplCRC = computeCrc32(in);
                } finally {
                    in.close();
                }
            }
        }
        return _currentJfxImplCRC.equals(crc);
    }

    public static boolean isProjectOpen(@NonNull Project project) {
        Project[] projects = OpenProjects.getDefault().getOpenProjects();
        if(projects != null) {
            for(Project p : Arrays.asList(projects)) {
                if(p.getProjectDirectory().getPath().equals(project.getProjectDirectory().getPath())) {
                    return true;
                }
            }
        }
        return false;
    }
    
    /**
     * Updates project.properties so that if JFXRT artifacts are explicitly added to
     * compile classpath if they are not on classpath by default. This is a workaround
     * of the fact that JDK1.7 does contain FX RT but does not provide it on classpath.
     * JDK1.8 has FX RT on classpath, but still may not include all relevant artifacts by default
     * and may need this extension.
     * Note that this extension is relevant not only for FX Application projects, but also
     * for SE projects that have the property "keep.javafx.runtime.on.classpath" set 
     * (see SE Deployment category in Project Properties dialog).
     * 
     * @param prj the project to update
     */
    public static void updateClassPathExtension(@NonNull final Project project) throws IOException {
        final boolean hasDefaultJavaFXPlatform[] = new boolean[1];
        final EditableProperties ep = new EditableProperties(true);
        final FileObject projPropsFO = project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
        if(projPropsFO != null) {
            try {
                ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() {
                    @Override
                    public Void run() throws Exception {
                        final Lookup lookup = project.getLookup();
                        final J2SEPropertyEvaluator eval = lookup.lookup(J2SEPropertyEvaluator.class);
                        if (eval != null) {
                            // if project has Default_JavaFX_Platform, change it to default Java Platform
                            String platformName = eval.evaluator().getProperty(JFXProjectProperties.PLATFORM_ACTIVE);
                            hasDefaultJavaFXPlatform[0] = JFXProjectProperties.isEqual(platformName, JavaFXPlatformUtils.DEFAULT_JAVAFX_PLATFORM);
                        }
                        if(hasDefaultJavaFXPlatform[0]) {
                            final J2SEProjectPlatform platformSetter = lookup.lookup(J2SEProjectPlatform.class);
                            if(platformSetter != null) {
                                platformSetter.setProjectPlatform(JavaPlatformManager.getDefault().getDefaultPlatform());
                            }
                        }
                        try (InputStream is = projPropsFO.getInputStream()) {
                            ep.load(is);
                        }
                        boolean cpExtUpdated = updateClassPathExtensionProperties(ep);
                        if (cpExtUpdated) {
                            final FileLock lock = projPropsFO.lock();
                            try (OutputStream os =projPropsFO.getOutputStream(lock)) {
                                ep.store(os);
                            } finally {                                                                
                                lock.releaseLock();
                            }
                        }
                        return null;
                    }
                });
            } catch (MutexException mux) {
                throw (IOException) mux.getException();
            }
            if(hasDefaultJavaFXPlatform[0]) {
                final String headerTemplate = NbBundle.getMessage(JFXProjectUtils.class, "TXT_UPDATED_DEFAULT_PLATFORM_HEADER"); //NOI18N
                final String header = MessageFormat.format(headerTemplate, new Object[] {ProjectUtils.getInformation(project).getDisplayName()});
                final String content = NbBundle.getMessage(JFXProjectUtils.class, "TXT_UPDATED_DEFAULT_PLATFORM_CONTENT"); //NOI18N
                Notification notePlatformChange = NotificationDisplayer.getDefault().notify(
                        header, 
                        ImageUtilities.loadImageIcon("org/netbeans/modules/javafx2/project/ui/resources/jfx_project.png", true), //NOI18N
                        content, 
                        null, 
                        NotificationDisplayer.Priority.LOW, 
                        NotificationDisplayer.Category.INFO);
                JFXProjectOpenedHook.addNotification(project, notePlatformChange);
            }
        } else {
            LOGGER.warning("Project metafiles inaccessible - classpath extension could not be verified and updated if needed."); //NOI18N
        }
    }

    /**
     * Update main class property in project metafiles in the current configuration
     * @param project
     * @param mainClass
     * @throws IOException 
     */
    public static void updatePropertyInActiveConfig(@NonNull final Project project, @NonNull final String propName, @NonNull final String propValue) throws IOException {
        final FileObject projectDir = project.getProjectDirectory();
        try {
            ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() {
                @Override
                public Void run() throws Exception {
                    final EditableProperties cep = new EditableProperties(true);
                    final FileObject configFO = projectDir.getFileObject(JFXProjectConfigurations.CONFIG_PROPERTIES_FILE);
                    if(configFO != null) {
                        final InputStream cis = configFO.getInputStream();
                        try {
                            cep.load(cis);
                        } finally {
                            if (cis != null) {
                                cis.close();
                            }
                        }
                    }
                    String config = cep.getProperty(ProjectProperties.PROP_PROJECT_CONFIGURATION_CONFIG);
                    
                    final FileObject projPropsFO = config == null ? projectDir.getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH) :
                            projectDir.getFileObject(JFXProjectConfigurations.PROJECT_CONFIGS_DIR + 
                            "/" + config + "." + JFXProjectConfigurations.PROPERTIES_FILE_EXT);  //NOI18N
                    assert projPropsFO != null : "Inaccessible file " + JFXProjectConfigurations.PROJECT_CONFIGS_DIR +  //NOI18N
                            "/" + config + "." + JFXProjectConfigurations.PROPERTIES_FILE_EXT;  //NOI18N
                    final InputStream is = projPropsFO.getInputStream();
                    final EditableProperties ep = new EditableProperties(true);
                    try {
                        ep.load(is);
                    } finally {
                        if (is != null) {
                            is.close();
                        }
                    }
                    ep.setProperty(propName, propValue);
                    OutputStream os = null;
                    FileLock lock = null;
                    try {
                        lock = projPropsFO.lock();
                        os = projPropsFO.getOutputStream(lock);
                        ep.store(os);
                    } finally {
                        if (os != null) {
                            os.close();
                        }
                        if (lock != null) {
                            lock.releaseLock();
                        }
                    }
                    return null;
                }
            });
        } catch (MutexException mux) {
            throw (IOException) mux.getException();
        }
    }

    /**
     * Create array of Strings representing path artifacts that can be set to path property;
     * all but the last String gets : appended.
     * @param artifacts
     * @return array of artifacts
     */
    public static String[] getPaths(@NonNull final Collection artifacts) {
        List l = new ArrayList();
        Iterator i = artifacts.iterator();
        while(i.hasNext()) {
            String s = i.next();
            if(i.hasNext()) {
                if(s.endsWith(":")) { //NOI18N
                    l.add(s);
                } else {
                    l.add(s + ":"); //NOI18N
                }
            } else {
                if(s.endsWith(":")) { //NOI18N
                    l.add(s.substring(0, s.length()-1));
                } else {
                    l.add(s);
                }
            }
        }
        return l.toArray(new String[0]);
    }
    
    /**
     * Remove trailing : from array of path artifacts and return the array as collection
     * @param artifacts
     * @return 
     */
    @NonNull
    public static Set getPaths(@NonNull final String[] artifacts) {        
        final Set l = new LinkedHashSet<>();
        for(String artifact : artifacts) {
            if(artifact.endsWith(":")) { //NOI18N
                l.add(artifact.substring(0, artifact.length()-1));
            } else {
                l.add(artifact);
            }
        }
        return l;
    }
    
    /**
     * Filter out artifacts that contain subString
     * @param artifacts
     * @return set without artifacts that contain subString
     */
    private static Set filterOutArtifacts(@NonNull final Collection artifacts, @NonNull String subString) {
        Set result = new LinkedHashSet();
        for(String artifact : artifacts) {
            if(!artifact.contains(subString)) {
                result.add(artifact);
            }
        }
        return result;
    }
    
    /**
     * Returns existing value of a path property, null if it does not exist
     * @param ep EditableProperties
     * @return collection of artifacts or null
     */
    @CheckForNull
    public static Set getExistingProperty(@NonNull final EditableProperties ep, @NonNull String propName) {
        // existing 
        String currentPropVal = ep.getProperty(propName);
        if(currentPropVal != null) {
            return Collections.unmodifiableSet(getPaths(PropertyUtils.tokenizePath(currentPropVal)));
        }
        return null;
    }
    
    /**
     * Returns updated value of classpath property, null if it should not exist
     * @param ep EditableProperties
     * @return collection of artifacts or null
     */
    public static Set getUpdatedCPProperty(@NonNull final EditableProperties ep, boolean extensionPropertyEmpty) {
        boolean extensionNeeded = JFXProjectProperties.isTrue(ep.getProperty(JFXProjectProperties.JAVAFX_ENABLED)) ||
                JFXProjectProperties.isTrue(ep.getProperty(JFXProjectProperties.JAVASE_KEEP_JFXRT_ON_CLASSPATH));
        // existing
        Set existing = getExistingProperty(ep, ProjectProperties.JAVAC_CLASSPATH);
        Set updated = new LinkedHashSet();
        if(existing != null) {
            updated = filterOutArtifacts(existing, "${javafx.runtime}"); // NOI18N
            updated = filterOutArtifacts(updated, JavaFXPlatformUtils.getClassPathExtensionProperty());
        }
        if(extensionNeeded && !extensionPropertyEmpty) {
            updated.add(JavaFXPlatformUtils.getClassPathExtensionProperty());
        }
        return Collections.unmodifiableSet(updated);
    }

    /**
     * Removes the path entry from path.
     * @param path to remove the netry from
     * @param toRemove the entry to be rmeoved from the path
     * @return new path with removed entry
     */
    @NonNull
    public static String removeFromPath(@NonNull final String path, @NonNull final String toRemove) {
        Parameters.notNull("path", path);   //NOI18N
        Parameters.notNull("toRemove", toRemove); //NOI18N
        final StringBuilder sb = new StringBuilder();
        for (String entry : PropertyUtils.tokenizePath(path)) {
            if (toRemove.equals(entry)) {
                continue;
            }
            sb.append(entry);
            sb.append(':'); //NOI18N
        }
        return sb.length() == 0 ?
            sb.toString() :
            sb.substring(0, sb.length()-1);
    }

    /**
     * Returns new value of FX artifacts extension property if it needs to be updated, null otherwise
     * @param ep EditableProperties
     * @return collection of artifacts or null
     */
    public static Set getUpdatedExtensionProperty(@NonNull final EditableProperties ep) throws IllegalArgumentException {
        boolean propertyNeeded = JFXProjectProperties.isTrue(ep.getProperty(JFXProjectProperties.JAVAFX_ENABLED)) ||
                JFXProjectProperties.isTrue(ep.getProperty(JFXProjectProperties.JAVASE_KEEP_JFXRT_ON_CLASSPATH));
        // expected
        Set updated = null;
        if(propertyNeeded) {
            String platformName = ep.getProperty(JFXProjectProperties.PLATFORM_ACTIVE);
            JavaPlatform platform = JavaFXPlatformUtils.findJavaPlatform(platformName);
            if(platform == null) {
                // platform does not exist, thus no extension property update is to take place
                return null;
            }
            updated = JavaFxRuntimeInclusion.getProjectClassPathExtension(platform);
        }
        return updated == null ? null : Collections.unmodifiableSet(updated);
    }

    /**
     * Compares two Set representations of a path property
     * @param set1
     * @param set2
     * @return true if the two sets represent equal existence and values of path properties
     */
    public static boolean pathPropertiesEqual(Set set1, Set set2) {
        if(set1 == null && set2 == null) {
            return true;
        }
        if(set1 == null || set2 == null) {
            return false;
        }
        return set1.containsAll(set2) && set2.containsAll(set1);
    }
    
    /**
     * Checks whether JFXRT artifacts are properly added to classpath and represented 
     * in project.properties if the current Java Platform needs this workaround
     * @param prj the project to check
     * @return true if project properties correctly represent JFXRT artifacts
     */
    public static boolean hasCorrectClassPathExtension(@NonNull final Project project) throws IOException, IllegalArgumentException  {
        final EditableProperties ep = readFromFile(
            project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH)
                );
        String platformName = ep.getProperty(JFXProjectProperties.PLATFORM_ACTIVE);
        if(JFXProjectProperties.isEqual(platformName, JavaFXPlatformUtils.DEFAULT_JAVAFX_PLATFORM)) {
            // request auto-replace of Default_JavaFX_Platform by default platform
            return false;
        }
        Set existingExt = getExistingProperty(ep, JavaFXPlatformUtils.JAVAFX_CLASSPATH_EXTENSION);
        Set updatedExt = getUpdatedExtensionProperty(ep);
        Set existingCP = getExistingProperty(ep, ProjectProperties.JAVAC_CLASSPATH);
        Set updatedCP = getUpdatedCPProperty(ep, updatedExt == null || updatedExt.isEmpty());
        return pathPropertiesEqual(existingExt, updatedExt) && pathPropertiesEqual(existingCP, updatedCP);
    }
    
    /**
     * Updates EditableProperties so that JFXRT artifacts are explicitly added to
     * compile classpath if they are not on classpath by default. This is a workaround
     * of the fact that JDK1.7 does contain FX RT but does not provide it on classpath.
     * JDK1.8 has FX RT on classpath, but still may not include all relevant artifacts by default
     * and may need this extension.
     * Note that this extension is relevant not only for FX Application projects, but also
     * for SE projects that have the property "keep.javafx.runtime.on.classpath" set 
     * (see SE Deployment category in Project Properties dialog).
     * 
     * @param ep EditableProperties containing properties to be updated
     */
    public static boolean updateClassPathExtensionProperties(@NonNull final EditableProperties ep) {
        boolean changed = false;
        changed = ep.remove(PROPERTY_JAVAFX_RUNTIME) != null ? true : changed;
        changed = ep.remove(PROPERTY_JAVAFX_SDK) != null ? true : changed;
        Collection extendExtProp = getUpdatedExtensionProperty(ep);
        if(extendExtProp != null && !extendExtProp.isEmpty()) {
            Collection currentCpExt = getExistingProperty(ep, JavaFXPlatformUtils.JAVAFX_CLASSPATH_EXTENSION);
            if (!extendExtProp.equals(currentCpExt)) {
                //update javafx.classpath.extension only in case that current value doesn't contain required jars
                ep.setProperty(JavaFXPlatformUtils.JAVAFX_CLASSPATH_EXTENSION, getPaths(extendExtProp));
                changed = true;
            }
        } else {
            changed = ep.remove(JavaFXPlatformUtils.JAVAFX_CLASSPATH_EXTENSION) != null ? true : changed;
            //ep.remove(JFXProjectProperties.JAVASE_KEEP_JFXRT_ON_CLASSPATH);
        }
        Collection extendCPProp = getUpdatedCPProperty(ep, extendExtProp == null || extendExtProp.isEmpty());
        // JAVAC_CLASSPATH to be preserved even if empty (create new project creates it empty by default)
        if(extendCPProp != null) {
            Collection currentJavacCp = getExistingProperty(ep, ProjectProperties.JAVAC_CLASSPATH);
            if (!extendCPProp.equals(currentJavacCp)) {
                ep.setProperty(ProjectProperties.JAVAC_CLASSPATH, getPaths(extendCPProp));
                changed = true;
            }
        }
        // Remove JavaFX endorsed.classpath entries if they are present (#235380, see also #214386)
        final String endorsedCp = ep.get(ProjectProperties.ENDORSED_CLASSPATH);
        if (endorsedCp != null && !endorsedCp.isEmpty()) {
            String[] cpElements = PropertyUtils.tokenizePath(endorsedCp);
            List updatedEndorsedCpList = new ArrayList();
            for (String element : cpElements) {
                if (!element.startsWith("${javafx.runtime}/")) { //NOI18N
                    updatedEndorsedCpList.add(element);
                }
            }
            String[] updatedEndorsedCp = updatedEndorsedCpList.toArray(new String[0]);
            for (int i = 0; i < updatedEndorsedCp.length - 1; i++) {
                updatedEndorsedCp[i] += ":"; //NOI18N
            }
            ep.setProperty(ProjectProperties.ENDORSED_CLASSPATH, updatedEndorsedCp);
            changed = true;
        }
        return changed;
    }

    /**
     * Checks the existence of default configuration for given RUN_AS type.
     * If it does not exist, it is created.
     * 
     * @param proj the project to check
     * @param runAs run type whose default configuration is to be updated
     * @param setBrowserProps if true, adds properties representing an existing browser
     * @return true is any update took place
     */
    public static boolean updateDefaultRunAsConfigFile(final @NonNull FileObject projDir, JFXProjectProperties.RunAsType runAs, boolean setBrowserProps) throws IOException {
        boolean updated = false;
        String configName = runAs.getDefaultConfig();
        String configFile = makeSafe(configName);
        String sharedPath = JFXProjectConfigurations.getSharedConfigFilePath(configFile);
        FileObject sharedCfgFO = projDir.getFileObject(sharedPath);
        final EditableProperties sharedCfgProps = sharedCfgFO != null ?
                readFromFile(sharedCfgFO) : new EditableProperties(true);
        assert sharedCfgProps != null;
        if(sharedCfgProps.isEmpty()) {
            sharedCfgProps.setProperty("$label", configName); // NOI18N
            sharedCfgProps.setComment("$label", new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_run_as_defaults")}, false); // NOI18N
            saveToFile(projDir, sharedPath, sharedCfgProps);
            updated = true;
        }
        String privatePath = JFXProjectConfigurations.getPrivateConfigFilePath(configFile);
        FileObject privateCfgFO = projDir.getFileObject(privatePath);
        final EditableProperties privateCfgProps = privateCfgFO != null ?
                readFromFile(projDir, privatePath) : new EditableProperties(true);
        assert privateCfgProps != null;
        if(privateCfgProps.isEmpty() || setBrowserProps) {
            privateCfgProps.setProperty(JFXProjectProperties.RUN_AS, runAs.getString());
            privateCfgProps.setComment(JFXProjectProperties.RUN_AS, new String[]{"# " + NbBundle.getMessage(JFXProjectUtils.class, "COMMENT_run_as_defaults")}, false); // NOI18N
            if(setBrowserProps) {
                Map browserInfo = getDefaultBrowserInfo();
                if(browserInfo != null && !browserInfo.isEmpty()) {
                    for(Map.Entry entry : browserInfo.entrySet()) {
                        privateCfgProps.setProperty(JFXProjectProperties.RUN_IN_BROWSER, entry.getKey());
                        privateCfgProps.setProperty(JFXProjectProperties.RUN_IN_BROWSER_PATH, entry.getValue());
                        break;
                    }
                }
            }
            saveToFile(projDir, privatePath, privateCfgProps);       
            updated = true;
        }
        return updated;
    }

    /**
     * @return name and path to default browser
     */
    public static Map getDefaultBrowserInfo() {
        Lookup.Result allBrowsers = Lookup.getDefault().lookupResult(ExtWebBrowser.class);
        Map browserPaths = new HashMap();
        for(Lookup.Item browser : allBrowsers.allItems()) {
            String name = browser.getDisplayName();
            if(name != null && name.toLowerCase().contains("default")) { // NOI18N
                NbProcessDescriptor proc = browser.getInstance().getBrowserExecutable();
                String path = proc.getProcessName();
                if(JFXProjectProperties.isNonEmpty(path)) {
                    browserPaths.put(name, path);
                }
                break;
            }
        }
        return browserPaths;
    }
    
    /**
     * Returns a copy of existing list of maps, usually used to store application parameters
     * 
     * @param list2Copy
     * @return copy of list2Copy
     */
    public static List> copyList(List> list2Copy) {
        List> list2Return = new ArrayList>();
        if(list2Copy != null ) {
            for (Map map : list2Copy) {
                list2Return.add(copyMap(map));
            }
        }
        return list2Return;
    }

    /**
     * Returns a copy of existing string map, usually used to store application parameter
     * (keys 'name' and 'value')
     * 
     * @param map2Copy
     * @return copy of map2Copy
     */
    public static Map copyMap(Map map2Copy) {
        Map newMap = new HashMap();
        if(map2Copy != null) {
            for(Map.Entry entry : map2Copy.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                newMap.put(key, value);
            }
        }
        return newMap;
    }

    /**
     * Modifies name so that it can be used as file name,
     * i.e., replaces problematic characters.
     * 
     * @param name
     * @return modified name usable as file name
     */
    public static String makeSafe(@NonNull String name) {
        return name.replaceAll("[^a-zA-Z0-9_.-]", "_"); // NOI18N;
    }

    public static EditableProperties readFromFile(final @NonNull Project project, final @NonNull String relativePath) throws IOException {
        final FileObject dirFO = project.getProjectDirectory();
        return readFromFile(dirFO, relativePath);
    }

    public static EditableProperties readFromFile(final @NonNull FileObject dirFO, final @NonNull String relativePath) throws IOException {
        assert dirFO.isFolder();
        final FileObject propsFO = dirFO.getFileObject(relativePath);
        return readFromFile(propsFO);
    }

    public static EditableProperties readFromFile(final @NonNull FileObject propsFO) throws IOException {
        final EditableProperties ep = new EditableProperties(true);
        if(propsFO != null) {
            assert propsFO.isData();
            try {
                ProjectManager.mutex().readAccess(new Mutex.ExceptionAction() {
                    @Override
                    public Void run() throws Exception {
                        final InputStream is = propsFO.getInputStream();
                        try {
                            ep.load(is);
                        } finally {
                            if (is != null) {
                                is.close();
                            }
                        }
                        return null;
                    }
                });
            } catch (MutexException mux) {
                throw (IOException) mux.getException();
            }
        }
        return ep;
    }

    public static void deleteFile(final @NonNull Project project, final @NonNull String relativePath) throws IOException {
        final FileObject propsFO = project.getProjectDirectory().getFileObject(relativePath);
        deleteFile(propsFO);
    }
    
    public static void deleteFile(final @NonNull FileObject dirFO, final @NonNull String relativePath) throws IOException {
        assert dirFO.isFolder();
        final FileObject propsFO = dirFO.getFileObject(relativePath);
        deleteFile(propsFO);
    }

    public static void deleteFile(final @NonNull FileObject propsFO) throws IOException {
        if(propsFO != null) {
            try {
                ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() {
                    @Override
                    public Void run() throws Exception {
                        propsFO.delete();
                        return null;
                    }
                });
            } catch (MutexException mux) {
                throw (IOException) mux.getException();
            }       
        }
    }

    public static void saveToFile(final @NonNull Project project, final @NonNull String relativePath, final @NonNull EditableProperties ep) throws IOException {
        FileObject dirFO = project.getProjectDirectory();
        saveToFile(dirFO, relativePath, ep);
    }
    
    public static void saveToFile(final @NonNull FileObject dirFO, final @NonNull String relativePath, final @NonNull EditableProperties ep) throws IOException {
        assert dirFO.isFolder();
        FileObject f = dirFO.getFileObject(relativePath);
        final FileObject propsFO;
        if(f == null) {
            propsFO = FileUtil.createData(dirFO, relativePath);
            assert propsFO != null : "FU.cD must not return null; called on " + dirFO + " + " + relativePath; // #50802  // NOI18N
        } else {
            propsFO = f;
        }
        saveToFile(propsFO, ep);
    }
    
    public static void saveToFile(final @NonNull FileObject propsFO, final @NonNull EditableProperties ep) throws IOException {
        if(propsFO != null) {
            assert propsFO.isData();
            try {
                ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction() {
                    @Override
                    public Void run() throws Exception {
                        OutputStream os = null;
                        FileLock lock = null;
                        try {
                            lock = propsFO.lock();
                            os = propsFO.getOutputStream(lock);
                            ep.store(os);
                        } finally {
                            if (os != null) {
                                os.close();
                            }
                            if (lock != null) {
                                lock.releaseLock();
                            }
                        }
                        return null;
                    }
                });
            } catch (MutexException mux) {
                throw (IOException) mux.getException();
            }
        }
    }

    private static final String MODULE_INFO = "module-info.java"; // NOI18N

    // as in org.netbeans.modules.maven.api.ModuleInfoUtils.hasModuleInfoInSource;
    public static boolean hasModuleInfo(SourceGroupSupport support) {
        for (SourceGroupProxy sourceGroup : support.getSourceGroups()) {
            if(sourceGroup.getRootFolder().getFileObject(MODULE_INFO) != null) {
                return true;
            }
        }
        return false;
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy