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

org.netbeans.modules.java.j2seproject.J2SEProject 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.java.j2seproject;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
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.project.JavaProjectConstants;
import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.ant.AntArtifact;
import org.netbeans.api.project.ant.AntBuildExtender;
import org.netbeans.api.project.libraries.Library;
import org.netbeans.api.project.libraries.LibraryManager;
import org.netbeans.api.queries.FileBuiltQuery.Status;
import org.netbeans.modules.java.api.common.Roots;
import org.netbeans.modules.java.api.common.SourceRoots;
import org.netbeans.modules.java.api.common.ant.UpdateHelper;
import org.netbeans.modules.java.api.common.classpath.ClassPathModifier;
import org.netbeans.modules.java.api.common.classpath.ClassPathProviderImpl;
import org.netbeans.modules.java.api.common.problems.ProjectProblemsProviders;
import org.netbeans.modules.java.api.common.project.ProjectConfigurations;
import org.netbeans.modules.java.api.common.project.ProjectHooks;
import org.netbeans.modules.java.api.common.project.ProjectOperations;
import org.netbeans.modules.java.api.common.project.ProjectProperties;
import org.netbeans.modules.java.api.common.project.ui.LogicalViewProviders;
import org.netbeans.modules.java.api.common.queries.QuerySupport;
import org.netbeans.modules.java.j2seproject.api.J2SEProjectBuilder;
import org.netbeans.modules.java.j2seproject.api.J2SEPropertyEvaluator;
import org.netbeans.modules.java.j2seproject.ui.customizer.CustomizerProviderImpl;
import org.netbeans.modules.java.j2seproject.ui.customizer.J2SECompositePanelProvider;
import org.netbeans.modules.java.j2seproject.ui.customizer.J2SEProjectProperties;
import org.netbeans.modules.java.j2seproject.ui.wizards.J2SEFileWizardIterator;
import org.netbeans.modules.project.ui.spi.TemplateCategorySorter;
import org.netbeans.spi.java.project.support.ExtraSourceJavadocSupport;
import org.netbeans.spi.java.project.support.LookupMergerSupport;
import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport;
import org.netbeans.spi.project.ActionProvider;
import org.netbeans.spi.project.AuxiliaryConfiguration;
import org.netbeans.spi.project.ant.AntArtifactProvider;
import org.netbeans.spi.project.ant.AntBuildExtenderFactory;
import org.netbeans.spi.project.support.LookupProviderSupport;
import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
import org.netbeans.spi.project.support.ant.AntBasedProjectRegistration;
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.PropertyProvider;
import org.netbeans.spi.project.support.ant.PropertyUtils;
import org.netbeans.spi.project.support.ant.ReferenceHelper;
import org.netbeans.spi.project.ui.PrivilegedTemplates;
import org.netbeans.spi.project.ui.RecommendedTemplates;
import org.netbeans.spi.project.ui.support.UILookupMergerSupport;
import org.netbeans.spi.queries.FileBuiltQueryImplementation;
import org.netbeans.spi.queries.FileEncodingQueryImplementation;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.ChangeSupport;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.netbeans.spi.whitelist.support.WhiteListQueryMergerSupport;
import org.openide.filesystems.URLMapper;
import org.openide.loaders.DataObject;
import org.openide.modules.SpecificationVersion;
import org.openide.util.HelpCtx;
import org.openide.util.Pair;
/**
 * Represents one plain J2SE project.
 * @author Jesse Glick, et al.
 */
@AntBasedProjectRegistration(
    type=J2SEProject.TYPE,
    iconResource="org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png", // NOI18N
    sharedName=J2SEProject.PROJECT_CONFIGURATION_NAME,
    sharedNamespace= J2SEProject.PROJECT_CONFIGURATION_NAMESPACE,
    privateName=J2SEProject.PRIVATE_CONFIGURATION_NAME,
    privateNamespace= J2SEProject.PRIVATE_CONFIGURATION_NAMESPACE
)
public final class J2SEProject implements Project {

    public static final String TYPE = "org.netbeans.modules.java.j2seproject"; // NOI18N
    static final String PROJECT_CONFIGURATION_NAME = "data"; // NOI18N
    public static final String PROJECT_CONFIGURATION_NAMESPACE = "http://www.netbeans.org/ns/j2se-project/3"; // NOI18N
    static final String PRIVATE_CONFIGURATION_NAME = "data"; // NOI18N
    static final String PRIVATE_CONFIGURATION_NAMESPACE = "http://www.netbeans.org/ns/j2se-project-private/1"; // NOI18N

    private static final String[] EXTENSIBLE_TARGETS = new String[] {
        "-do-init",             //NOI18N
        "-init-check",          //NOI18N
        "-post-clean",          //NOI18N
        "-pre-pre-compile",     //NOI18N
        "-do-compile",          //NOI18N
        "-do-compile-single",   //NOI18N
        "jar",                  //NOI18N
        "-post-jar",            //NOI18N
        "run",                  //NOI18N
        "debug",                //NOI18N
        "profile",              //NOI18N
    };
    private static final Icon J2SE_PROJECT_ICON = ImageUtilities.loadImageIcon("org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png", false); // NOI18N
    private static final Logger LOG = Logger.getLogger(J2SEProject.class.getName());

    private final AuxiliaryConfiguration aux;
    private final AntProjectHelper helper;
    private final PropertyEvaluator eval;
    private final ReferenceHelper refHelper;
    private final GeneratedFilesHelper genFilesHelper;
    private Lookup lookup;
    private final UpdateHelper updateHelper;
    private MainClassUpdater mainClassUpdater;
    private SourceRoots sourceRoots;
    private SourceRoots testRoots;
    private final ClassPathProviderImpl cpProvider;
    private final ClassPathModifier cpMod;

    private AntBuildExtender buildExtender;

    /**
     * @see J2SEProject.ProjectXmlSavedHookImpl#projectXmlSaved()
     */
    private final ThreadLocal projectPropertiesSave;

    @SuppressWarnings("LeakingThisInConstructor")
    public J2SEProject(AntProjectHelper helper) throws IOException {
        this.projectPropertiesSave = new ThreadLocal() {
            @Override
            protected Boolean initialValue() {
                return Boolean.FALSE;
            }
        };
        this.helper = helper;
        aux = helper.createAuxiliaryConfiguration();
        UpdateProjectImpl updateProject = new UpdateProjectImpl(this, helper, aux);
        this.updateHelper = new UpdateHelper(updateProject, helper);
        eval = ProjectConfigurations.createPropertyEvaluator(this, helper, UPDATE_PROPERTIES);
        for (int v = 4; v < 10; v++) {
            if (aux.getConfigurationFragment("data", "http://www.netbeans.org/ns/j2se-project/" + v, true) != null) { // NOI18N
                throw Exceptions.attachLocalizedMessage(new IOException("too new"), // NOI18N
                        NbBundle.getMessage(J2SEProject.class, "J2SEProject.too_new", FileUtil.getFileDisplayName(helper.getProjectDirectory())));
            }
        }
        refHelper = new ReferenceHelper(helper, aux, evaluator());
        buildExtender = AntBuildExtenderFactory.createAntExtender(new J2SEExtenderImplementation(), refHelper);
        genFilesHelper = new GeneratedFilesHelper(helper, buildExtender);

        this.cpProvider = ClassPathProviderImpl.Builder.create(helper, evaluator(), getSourceRoots(), getTestSourceRoots())
                .setProject(this)
                .build();
        this.cpMod = new ClassPathModifier(this, this.updateHelper, evaluator(), refHelper, null, createClassPathModifierCallback(), null);
        lookup = createLookup(aux, newProjectOperationsCallback(this, updateProject));
    }

    private ClassPathModifier.Callback createClassPathModifierCallback() {
        return new ClassPathModifier.Callback() {
            @Override
            public String getClassPathProperty(SourceGroup sg, String type) {
                assert sg != null : "SourceGroup cannot be null";  //NOI18N
                assert type != null : "Type cannot be null";  //NOI18N
                final String[] classPathProperty = getClassPathProvider().getPropertyName (sg, type);
                if (classPathProperty == null || classPathProperty.length == 0) {
                    throw new UnsupportedOperationException ("Modification of [" + sg.getRootFolder().getPath() +", " + type + "] is not supported"); //NOI18N
                }
                return classPathProperty[0];
            }

            @Override
            public String getElementName(String classpathProperty) {
                return null;
            }
        };
    }

    /**
     * Returns the project directory
     * @return the directory the project is located in
     */
    @Override
    public FileObject getProjectDirectory() {
        return helper.getProjectDirectory();
    }

    @Override
    public String toString() {
        return "J2SEProject[" + FileUtil.getFileDisplayName(getProjectDirectory()) + "]"; // NOI18N
    }
        
    private static final PropertyProvider UPDATE_PROPERTIES;
    static {
        Map defs = new HashMap();

        defs.put(ProjectProperties.ANNOTATION_PROCESSING_ENABLED, "true"); //NOI18N
        defs.put(ProjectProperties.ANNOTATION_PROCESSING_ENABLED_IN_EDITOR, "false"); //NOI18N
        defs.put(ProjectProperties.ANNOTATION_PROCESSING_RUN_ALL_PROCESSORS, "true"); //NOI18N
        defs.put(ProjectProperties.ANNOTATION_PROCESSING_PROCESSORS_LIST, ""); //NOI18N
        defs.put(ProjectProperties.ANNOTATION_PROCESSING_SOURCE_OUTPUT, "${build.generated.sources.dir}/ap-source-output"); //NOI18N
        defs.put(ProjectProperties.JAVAC_PROCESSORPATH,"${" + ProjectProperties.JAVAC_CLASSPATH + "}"); //NOI18N
        defs.put("javac.test.processorpath", "${" + ProjectProperties.JAVAC_TEST_CLASSPATH + "}"); // NOI18N

        UPDATE_PROPERTIES = PropertyUtils.fixedPropertyProvider(defs);
    }

    public PropertyEvaluator evaluator() {
        assert eval != null;
        return eval;
    }

    public ReferenceHelper getReferenceHelper () {
        return this.refHelper;
    }

    public UpdateHelper getUpdateHelper() {
        return this.updateHelper;
    }

    @Override
    public Lookup getLookup() {
        return lookup;
    }

    public AntProjectHelper getAntProjectHelper() {
        return helper;
    }

    private Lookup createLookup(final AuxiliaryConfiguration aux, final ProjectOperations.Callback opsCallback) {
        final PlatformChangedHook platformChangedHook = new PlatformChangedHook();
        final FileEncodingQueryImplementation encodingQuery = QuerySupport.createFileEncodingQuery(evaluator(), ProjectProperties.SOURCE_ENCODING);
        final Lookup base = Lookups.fixed(
            J2SEProject.this,
            QuerySupport.createProjectInformation(updateHelper, this, J2SE_PROJECT_ICON),
            aux,
            helper.createCacheDirectoryProvider(),
            helper.createAuxiliaryProperties(),
            refHelper.createSubprojectProvider(),
            LogicalViewProviders.createBuilder(
                this,
                eval,
                "org-netbeans-modules-java-j2seproject").   //NOI18N
                setHelpCtx(new HelpCtx("org.netbeans.modules.java.j2seproject.ui.J2SELogicalViewProvider.J2SELogicalViewRootNode")).    //NOI18N
                setCompileOnSaveBadge(newCoSBadge()).
                build(),
            // new J2SECustomizerProvider(this, this.updateHelper, evaluator(), refHelper),
            new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper),        
            LookupMergerSupport.createClassPathProviderMerger(cpProvider),
            QuerySupport.createCompiledSourceForBinaryQuery(helper, evaluator(), getSourceRoots(), getTestSourceRoots()),
            QuerySupport.createJavadocForBinaryQuery(helper, evaluator()),
            new AntArtifactProviderImpl(),
            ProjectHooks.createProjectXmlSavedHookBuilder(eval, updateHelper, genFilesHelper).
                    setBuildImplTemplate(J2SEProject.class.getResource("resources/build-impl.xsl")).    //NOI18N
                    setBuildTemplate(J2SEProject.class.getResource("resources/build.xsl")).             //NOI18N
                    setOverrideModifiedBuildImplPredicate(new Callable(){
                        @Override
                        public Boolean call() throws Exception {
                            return projectPropertiesSave.get();
                        }
                    }).
                    build(),
            UILookupMergerSupport.createProjectOpenHookMerger(
                ProjectHooks.createProjectOpenedHookBuilder(this, eval, updateHelper, genFilesHelper, cpProvider).
                        addClassPathType(ClassPath.BOOT).
                        addClassPathType(ClassPath.COMPILE).
                        addClassPathType(ClassPath.SOURCE).
                        addClassPathType(JavaClassPathConstants.MODULE_COMPILE_PATH).   //For DefaultClassPathProvider
                        setBuildImplTemplate(J2SEProject.class.getResource("resources/build-impl.xsl")).    //NOI18N
                        setBuildTemplate(J2SEProject.class.getResource("resources/build.xsl")).             //NOI18N
                        addOpenPostAction(newStartMainUpdaterAction()).
                        addOpenPostAction(newWebServicesAction()).
                        addOpenPostAction(newMissingPropertiesAction()).
                        addOpenPostAction(newUpdateCopyLibsAction()).
                        addClosePostAction(newStopMainUpdaterAction()).
                        build()),
            QuerySupport.createUnitTestForSourceQuery(getSourceRoots(), getTestSourceRoots()),
            QuerySupport.createSourceLevelQuery2(evaluator()),
            QuerySupport.createSources(this, helper, evaluator(), getSourceRoots(), getTestSourceRoots(), Roots.nonSourceRoots(ProjectProperties.BUILD_DIR, ProjectProperties.DIST_DIR)),
            QuerySupport.createSharabilityQuery2(helper, evaluator(), getSourceRoots(), getTestSourceRoots()),
            new CoSAwareFileBuiltQueryImpl(QuerySupport.createFileBuiltQuery(helper, evaluator(), getSourceRoots(), getTestSourceRoots()), this),
            new RecommendedTemplatesImpl (this.updateHelper),
            ProjectClassPathModifier.extenderForModifier(cpMod),
            buildExtender,
            cpMod,
            ProjectOperations.createBuilder(this, eval, updateHelper, refHelper, sourceRoots, testRoots).
                    addDataFiles("manifest.mf","master-application.jnlp","master-applet.jnlp","master-component.jnlp","preview-application.html","preview-applet.html").    //NOI18N
                    addMetadataFiles("xml-resources","catalog.xml").    //NOI18N
                    addPreservedPrivateProperties(ProjectProperties.APPLICATION_ARGS, ProjectProperties.RUN_WORK_DIR, ProjectProperties.COMPILE_ON_SAVE, ProjectProperties.DO_JLINK, ProjectProperties.JLINK_STRIP).
                    addUpdatedNameProperty(ProjectProperties.DIST_JAR, "$'{'dist.dir'}'/{0}.jar", true).    //NOI18N
                    addUpdatedNameProperty(J2SEProjectProperties.APPLICATION_TITLE, "{0}", false).  //NOI18N
                    addUpdatedNameProperty(ProjectProperties.DIST_JLINK_OUTPUT, "$'{'"+ProjectProperties.DIST_JLINK_DIR+"'}'/{0}", true).    //NOI18N
                    addUpdatedNameProperty(ProjectProperties.JLINK_LAUNCHER_NAME, "{0}", true).    //NOI18N
                    setCallback(opsCallback).
                    build(),
            ProjectConfigurations.createConfigurationProviderBuilder(this, eval, updateHelper).
                    addConfigurationsAffectActions(ActionProvider.COMMAND_RUN, ActionProvider.COMMAND_DEBUG).
                    setCustomizerAction(newConfigCustomizerAction()).
                    build(),
            new J2SEPersistenceProvider(this, cpProvider),
            UILookupMergerSupport.createPrivilegedTemplatesMerger(),
            UILookupMergerSupport.createRecommendedTemplatesMerger(),
            LookupProviderSupport.createSourcesMerger(),
            encodingQuery,
            new J2SEPropertyEvaluatorImpl(evaluator()),
            QuerySupport.createTemplateAttributesProvider(helper, encodingQuery),
            ExtraSourceJavadocSupport.createExtraSourceQueryImplementation(this, helper, evaluator()),
            LookupMergerSupport.createSFBLookupMerger(),
            ExtraSourceJavadocSupport.createExtraJavadocQueryImplementation(this, helper, evaluator()),
            LookupMergerSupport.createJFBLookupMerger(),
            QuerySupport.createBinaryForSourceQueryImplementation(this.sourceRoots, this.testRoots, this.helper, this.evaluator()), //Does not use APH to get/put properties/cfgdata
            QuerySupport.createAnnotationProcessingQuery(this.helper, this.evaluator(), ProjectProperties.ANNOTATION_PROCESSING_ENABLED, ProjectProperties.ANNOTATION_PROCESSING_ENABLED_IN_EDITOR, ProjectProperties.ANNOTATION_PROCESSING_RUN_ALL_PROCESSORS, ProjectProperties.ANNOTATION_PROCESSING_PROCESSORS_LIST, ProjectProperties.ANNOTATION_PROCESSING_SOURCE_OUTPUT, ProjectProperties.ANNOTATION_PROCESSING_PROCESSOR_OPTIONS),
            LookupProviderSupport.createActionProviderMerger(),
            WhiteListQueryMergerSupport.createWhiteListQueryMerger(),
            BrokenReferencesSupport.createReferenceProblemsProvider(helper, refHelper, eval, platformChangedHook, J2SEProjectUtil.getBreakableProperties(this), new String[]{ProjectProperties.PLATFORM_ACTIVE}),
            BrokenReferencesSupport.createPlatformVersionProblemProvider(helper, eval, platformChangedHook, JavaPlatform.getDefault().getSpecification().getName(), ProjectProperties.PLATFORM_ACTIVE, ProjectProperties.JAVAC_SOURCE, ProjectProperties.JAVAC_TARGET),
            BrokenReferencesSupport.createProfileProblemProvider(helper, refHelper, eval, ProjectProperties.JAVAC_PROFILE, ProjectProperties.RUN_CLASSPATH, ProjectProperties.ENDORSED_CLASSPATH),
            UILookupMergerSupport.createProjectProblemsProviderMerger(),
            new J2SEProjectPlatformImpl(this),
            QuerySupport.createCompilerOptionsQuery(eval, ProjectProperties.JAVAC_COMPILERARGS),
            QuerySupport.createUnitTestsCompilerOptionsQuery(eval, sourceRoots, testRoots),
            QuerySupport.createAutomaticModuleNameQuery(helper, eval, sourceRoots, ProjectProperties.MANIFEST_FILE),
            QuerySupport.createModuleInfoAccessibilityQuery(sourceRoots, testRoots),
            LookupMergerSupport.createCompilerOptionsQueryMerger(),
            J2SEFileWizardIterator.create(),
            ProjectProblemsProviders.createMissingModuleProjectProblemsProvider(this)
        );
        lookup = base; // in case LookupProvider's call Project.getLookup
        return LookupProviderSupport.createCompositeLookup(base, "Projects/org-netbeans-modules-java-j2seproject/Lookup"); //NOI18N
    }

    public ClassPathProviderImpl getClassPathProvider () {
        return this.cpProvider;
    }

    public ClassPathModifier getProjectClassPathModifier () {
        return this.cpMod;
    }

    // Package private methods -------------------------------------------------

    /**
     * Returns the source roots of this project
     * @return project's source roots
     */
    public synchronized SourceRoots getSourceRoots() {
        if (this.sourceRoots == null) { //Local caching, no project metadata access
            this.sourceRoots = SourceRoots.create(updateHelper, evaluator(), getReferenceHelper(),
                    J2SEProject.PROJECT_CONFIGURATION_NAMESPACE, "source-roots", false, "src.{0}{1}.dir"); //NOI18N
       }
        return this.sourceRoots;
    }

    public synchronized SourceRoots getTestSourceRoots() {
        if (this.testRoots == null) { //Local caching, no project metadata access
            this.testRoots = SourceRoots.create(updateHelper, evaluator(), getReferenceHelper(),
                    J2SEProject.PROJECT_CONFIGURATION_NAMESPACE, "test-roots", true, "test.{0}{1}.dir"); //NOI18N
        }
        return this.testRoots;
    }

    File getTestClassesDirectory() {
        String testClassesDir = evaluator().getProperty(ProjectProperties.BUILD_TEST_CLASSES_DIR);
        if (testClassesDir == null) {
            return null;
        }
        return helper.resolveFile(testClassesDir);
    }

    // Currently unused (but see #47230):
    /** Store configured project name. */
    public void setName(final String name) {
        ProjectManager.mutex().writeAccess(new Mutex.Action() {
            @Override
            public Void run() {
                Element data = updateHelper.getPrimaryConfigurationData(true);
                // XXX replace by XMLUtil when that has findElement, findText, etc.
                NodeList nl = data.getElementsByTagNameNS(J2SEProject.PROJECT_CONFIGURATION_NAMESPACE, "name");
                Element nameEl;
                if (nl.getLength() == 1) {
                    nameEl = (Element) nl.item(0);
                    NodeList deadKids = nameEl.getChildNodes();
                    while (deadKids.getLength() > 0) {
                        nameEl.removeChild(deadKids.item(0));
                    }
                } else {
                    nameEl = data.getOwnerDocument().createElementNS(J2SEProject.PROJECT_CONFIGURATION_NAMESPACE, "name");
                    data.insertBefore(nameEl, /* OK if null */data.getChildNodes().item(0));
                }
                nameEl.appendChild(data.getOwnerDocument().createTextNode(name));
                updateHelper.putPrimaryConfigurationData(data, true);
                return null;
            }
        });
    }


    /**
     * J2SEProjectProperties helper method to notify ProjectXmlSavedHookImpl about customizer save
     * @see J2SEProject.ProjectXmlSavedHookImpl#projectXmlSaved()
     * @param value true = active
     */
    public void setProjectPropertiesSave(boolean value) {
        this.projectPropertiesSave.set(value);
    }

    // Private innerclasses ----------------------------------------------------    
    /**
     * Exports the main JAR as an official build product for use from other scripts.
     * The type of the artifact will be {@link AntArtifact#TYPE_JAR}.
     */
    private final class AntArtifactProviderImpl implements AntArtifactProvider {

        @Override
        public AntArtifact[] getBuildArtifacts() {
            return new AntArtifact[] {
                helper.createSimpleAntArtifact(JavaProjectConstants.ARTIFACT_TYPE_JAR, "dist.jar", evaluator(), "jar", "clean", ProjectProperties.BUILD_SCRIPT), // NOI18N
            };
        }

    }

    private static final class RecommendedTemplatesImpl implements RecommendedTemplates, PrivilegedTemplates, TemplateCategorySorter {
        RecommendedTemplatesImpl (UpdateHelper helper) {
            this.helper = helper;
        }

        private UpdateHelper helper;

        // List of primarily supported templates

        private static final String[] APPLICATION_TYPES = new String[] {
            "java-classes",         // NOI18N
            "java-main-class",      // NOI18N
            "java-forms",           // NOI18N
            "gui-java-application", // NOI18N
            "java-beans",           // NOI18N
            "persistence",          // NOI18N
            "oasis-XML-catalogs",   // NOI18N
            "XML",                  // NOI18N
            "ant-script",           // NOI18N
            "ant-task",             // NOI18N
            "web-service-clients",  // NOI18N
            "REST-clients",         // NOI18N
            "wsdl",                 // NOI18N
            // "servlet-types",     // NOI18N
            // "web-types",         // NOI18N
            "junit",                // NOI18N
            // "MIDP",              // NOI18N
            "simple-files"          // NOI18N
        };

        private static final String[] LIBRARY_TYPES = new String[] {
            "java-classes",         // NOI18N
            "java-main-class",      // NOI18N
            "java-forms",           // NOI18N
            //"gui-java-application", // NOI18N
            "java-beans",           // NOI18N
            "persistence",          // NOI18N
            "oasis-XML-catalogs",   // NOI18N
            "XML",                  // NOI18N
            "ant-script",           // NOI18N
            "ant-task",             // NOI18N
            "servlet-types",        // NOI18N
            "servlet-types-j2se-only",// NOI18N
            "web-service-clients",  // NOI18N
            "REST-clients",         // NOI18N
            "wsdl",                 // NOI18N
            // "web-types",         // NOI18N
            "junit",                // NOI18N
            // "MIDP",              // NOI18N
            "simple-files"         // NOI18N
        };

        private static final String[] PRIVILEGED_NAMES = new String[] {
            "Templates/Classes/Class.java", // NOI18N
            "Templates/Classes/Package", // NOI18N
            "Templates/Classes/Interface.java", // NOI18N
            "Templates/GUIForms/JPanel.java", // NOI18N
            "Templates/GUIForms/JFrame.java", // NOI18N
            "Templates/Persistence/Entity.java", // NOI18N
            "Templates/Persistence/RelatedCMP", // NOI18N
            "Templates/WebServices/WebServiceClient"   // NOI18N
        };

        private static final Map  CAT_MAP;
        static {
            final Map m = new HashMap<>();
            m.put("Classes",0);     //NOI18N
            m.put("GUIForms",1);    //NOI18N
            m.put("Beans",2);       //NOI18N
            m.put("AWTForms",3);    //NOI18N
            m.put("UnitTests",4);   //NOI18N
            CAT_MAP = Collections.unmodifiableMap(m);
        };

        @Override
        public String[] getRecommendedTypes() {            
            return isLibrary() ? LIBRARY_TYPES : APPLICATION_TYPES;
        }

        @Override
        public String[] getPrivilegedTemplates() {
            return PRIVILEGED_NAMES;
        }

        @Override
        @NonNull
        public List sort(@NonNull final List original) {
            if (!isLibrary()) {
                return original;
            }
            final List result = new ArrayList<>(Collections.nCopies(CAT_MAP.size(), null));
            for (DataObject dobj : original) {
                final String name = dobj.getName();
                final Integer index = CAT_MAP.get(name);
                if (index == null) {
                    result.add(dobj);
                } else {
                    result.set (index, dobj);
                }
            }
            return filterNulls(result);
        }

        @NonNull
        private List filterNulls(@NonNull final List list) {
            boolean hasNull = false;
            for (int i=0; i result = new ArrayList<>(list.size());
            for (DataObject dobj : list) {
                if (dobj != null) {
                    result.add(dobj);
                }
            }
            return result;
        }

        private boolean isLibrary() {
            final EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
            // if the project has no main class, it's not really an application
            return ep.getProperty (ProjectProperties.MAIN_CLASS) == null || "".equals (ep.getProperty (ProjectProperties.MAIN_CLASS)); // NOI18N
        }

    }

    private static final class J2SEPropertyEvaluatorImpl implements J2SEPropertyEvaluator {
        private PropertyEvaluator evaluator;
        public J2SEPropertyEvaluatorImpl (PropertyEvaluator eval) {
            evaluator = eval;
        }
        @Override
        public PropertyEvaluator evaluator() {
            return evaluator;
        }
    }

    private class J2SEExtenderImplementation implements AntBuildExtenderImplementation {
        //add targets here as required by the external plugins..
        @Override
        public List getExtensibleTargets() {
            return Arrays.asList(EXTENSIBLE_TARGETS);
        }

        @Override
        public Project getOwningProject() {
            return J2SEProject.this;
        }

    }

    private static final DocumentBuilder db;
    static {
        try {
            db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new AssertionError(e);
        }
    }
    private static Document createNewDocument() {
        // #50198: for thread safety, use a separate document.
        // Using XMLUtil.createDocument is much too slow.
        synchronized (db) {
            return db.newDocument();
        }
    }

    private static final class CoSAwareFileBuiltQueryImpl implements FileBuiltQueryImplementation, PropertyChangeListener {

        private final FileBuiltQueryImplementation delegate;
        private final J2SEProject project;
        private final AtomicBoolean cosEnabled = new AtomicBoolean();
        private final Map> file2Status = new WeakHashMap>();

        @SuppressWarnings("LeakingThisInConstructor")
        public CoSAwareFileBuiltQueryImpl(FileBuiltQueryImplementation delegate, J2SEProject project) {
            this.delegate = delegate;
            this.project = project;

            project.evaluator().addPropertyChangeListener(this);

            setCoSEnabledAndXor();
        }

        private synchronized StatusImpl readFromCache(FileObject file) {
            Reference r = file2Status.get(file);

            return r != null ? r.get() : null;
        }

        @Override
        public Status getStatus(FileObject file) {
            StatusImpl result = readFromCache(file);

            if (result != null) {
                return result;
            }

            Status status = delegate.getStatus(file);

            if (status == null) {
                return null;
            }

            synchronized (this) {
                StatusImpl foisted = readFromCache(file);

                if (foisted != null) {
                    return foisted;
                }

                file2Status.put(file, new WeakReference(result = new StatusImpl(cosEnabled, status)));
            }

            return result;
        }

        boolean setCoSEnabledAndXor() {
            boolean nue = J2SEProjectUtil.isCompileOnSaveEnabled(project);
            boolean old = cosEnabled.getAndSet(nue);

            return old != nue;
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (!setCoSEnabledAndXor()) {
                return ;
            }

            Collection> toRefresh;

            synchronized (this) {
                toRefresh = new LinkedList>(file2Status.values());
            }

            for (Reference r : toRefresh) {
                StatusImpl s = r.get();

                if (s != null) {
                    s.stateChanged(null);
                }
            }
        }

        private static final class StatusImpl implements Status, ChangeListener {

            private final ChangeSupport cs = new ChangeSupport(this);
            private final AtomicBoolean cosEnabled;
            private final Status delegate;

            @SuppressWarnings("LeakingThisInConstructor")
            public StatusImpl(AtomicBoolean cosEnabled, Status delegate) {
                this.cosEnabled = cosEnabled;
                this.delegate = delegate;
                this.delegate.addChangeListener(this);
            }

            @Override
            public boolean isBuilt() {
                return cosEnabled.get() || delegate.isBuilt();
            }

            @Override
            public void addChangeListener(ChangeListener l) {
                cs.addChangeListener(l);
            }

            @Override
            public void removeChangeListener(ChangeListener l) {
                cs.removeChangeListener(l);
            }

            @Override
            public void stateChanged(ChangeEvent e) {
                cs.fireChange();
            }

        }
    }

    private final class PlatformChangedHook implements BrokenReferencesSupport.PlatformUpdatedCallBack {
        @Override
        public void platformPropertyUpdated(@NonNull final JavaPlatform platform) {
            J2SEProjectPlatformImpl.updateProjectXml(platform, updateHelper);
        }
    }

    @NonNull
    private Runnable newStartMainUpdaterAction() {
        return new Runnable() {
            @Override
            public void run() {
                //register updater of main.class
                //the updater is active only on the opened projects
                mainClassUpdater = new MainClassUpdater (
                        J2SEProject.this,
                        evaluator(),
                        updateHelper,
                        cpProvider.getProjectClassPaths(ClassPath.SOURCE)[0],
                        ProjectProperties.MAIN_CLASS);
                mainClassUpdater.start();
            }
        };
    }

    @NonNull
    private Runnable newStopMainUpdaterAction() {
        return new Runnable() {
            @Override
            public void run() {
                if (mainClassUpdater != null) {
                    mainClassUpdater.stop();
                    mainClassUpdater = null;
                }
            }
        };
    }

    @NonNull
    private Runnable newWebServicesAction() {
        return new Runnable() {
            private final String JAX_RPC_NAMESPACE="http://www.netbeans.org/ns/j2se-project/jax-rpc"; //NOI18N
            private final String JAX_RPC_CLIENTS="web-service-clients"; //NOI18N
            private final String JAX_RPC_CLIENT="web-service-client"; //NOI18N
            @Override
            public void run() {
                //remove jaxws.endorsed.dir property
                EditableProperties ep = updateHelper.getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
                ep.remove("jaxws.endorsed.dir");
                // move web-service-clients one level up from in project.xml
                // WS should be part of auxiliary configuration
                Element data = helper.getPrimaryConfigurationData(true);
                NodeList nodes = data.getElementsByTagName(JAX_RPC_CLIENTS);
                if(nodes.getLength() > 0) {
                    Element oldJaxRpcClients = (Element) nodes.item(0);
                    Document doc = createNewDocument();
                    Element newJaxRpcClients = doc.createElementNS(JAX_RPC_NAMESPACE, JAX_RPC_CLIENTS);
                    NodeList childNodes = oldJaxRpcClients.getElementsByTagName(JAX_RPC_CLIENT);
                    for (int i=0;i 0);
                helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, ep);
            }
        };
    }

    @NonNull
    private Runnable newUpdateCopyLibsAction() {
        return new Runnable() {
            private final String LIB_COPY_LIBS = "CopyLibs"; //NOI18N
            private final String PROP_VERSION = "version";   //NOI18N
            private final String VOL_CP = "classpath";       //NOI18N

            @Override
            public void run() {
                final LibraryManager projLibManager = refHelper.getProjectLibraryManager();
                if (projLibManager == null) {
                    return;
                }
                final Library globalCopyLibs = LibraryManager.getDefault().getLibrary(LIB_COPY_LIBS);
                final Library projectCopyLibs = projLibManager.getLibrary(LIB_COPY_LIBS);
                if (globalCopyLibs == null || projectCopyLibs == null) {
                    return;
                }
                final String globalStr = globalCopyLibs.getProperties().get(PROP_VERSION);
                if (globalStr == null) {
                    return;
                }
                try {
                    final SpecificationVersion globalVersion = new SpecificationVersion(globalStr);
                    final String projectStr = projectCopyLibs.getProperties().get(PROP_VERSION);
                    if (projectStr != null && globalVersion.compareTo(new SpecificationVersion(projectStr)) <= 0) {
                        return;
                    }

                    final List content = projectCopyLibs.getContent(VOL_CP);
                    projLibManager.removeLibrary(projectCopyLibs);
                    final FileObject projLibLoc = URLMapper.findFileObject(projLibManager.getLocation());
                    if (projLibLoc != null) {
                        final FileObject libFolder = projLibLoc.getParent();
                        boolean canDelete = libFolder.canWrite();
                        FileObject container = null;
                        for (URL u : content) {
                            FileObject fo = toFile(u);
                            if (fo != null) {
                                canDelete &= fo.canWrite();
                                if (container == null) {
                                    container = fo.getParent();
                                    canDelete &= container.canWrite();
                                    canDelete &= LIB_COPY_LIBS.equals(container.getName());
                                    canDelete &= libFolder.equals(container.getParent());
                                } else {
                                    canDelete &= container.equals(fo.getParent());
                                }
                            }
                        }
                        if (canDelete && container != null) {
                            container.delete();
                        }
                    }
                    refHelper.copyLibrary(globalCopyLibs);

                } catch (IllegalArgumentException iae) {
                    LOG.log(
                            Level.WARNING,
                            "Cannot update {0} due to invalid version.", //NOI18N
                            projectCopyLibs.getDisplayName());
                } catch (IOException ioe) {
                    Exceptions.printStackTrace(ioe);
                }
            }

            @CheckForNull
            private FileObject toFile(@NonNull final URL url) {
                final URL file = FileUtil.getArchiveFile(url);
                return URLMapper.findFileObject(file != null ? file : url);
            }
        };
    }

    @NonNull
    private Runnable newConfigCustomizerAction() {
        return new Runnable() {
            @Override
            public void run() {
                J2SEProject.this.getLookup().lookup(CustomizerProviderImpl.class).
                    showCustomizer(J2SECompositePanelProvider.RUN);
            }
        };
    }

    @NonNull
    private LogicalViewProviders.CompileOnSaveBadge newCoSBadge() {
        return new LogicalViewProviders.CompileOnSaveBadge() {
            @Override
            public boolean isBadgeVisible() {
                return !J2SEProjectUtil.isCompileOnSaveEnabled(J2SEProject.this) &&
                    J2SEProjectUtil.isCompileOnSaveSupported(J2SEProject.this);
            }
            @Override
            public boolean isImportant(@NonNull final String propertyName) {
                return ProjectProperties.COMPILE_ON_SAVE.equals(propertyName) ||
                propertyName.startsWith(ProjectProperties.COMPILE_ON_SAVE_UNSUPPORTED_PREFIX);
            }
        };
    }

    @NonNull
    private static ProjectOperations.Callback newProjectOperationsCallback (
        @NonNull final J2SEProject project,
        @NonNull final UpdateProjectImpl projectUpdate) {
        return new ProjectOperations.Callback() {
            @Override
            public void beforeOperation(@NonNull final ProjectOperations.Callback.Operation operation) {
            }
            @Override
            @SuppressWarnings("fallthrough")
            public void afterOperation(
                    @NonNull final ProjectOperations.Callback.Operation operation,
                    @NullAllowed final String newName,
                    @NullAllowed final Pair oldProject) {
                switch (operation) {
                    case COPY:
                        projectUpdate.setTransparentUpdate(true);
                    case MOVE:
                    case RENAME:
                        project.setName(newName);
                }
            }
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy