blob: 7ec8541c48c4754f878136941d109d362a2e19db [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.java.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<Boolean> projectPropertiesSave;
@SuppressWarnings("LeakingThisInConstructor")
public J2SEProject(AntProjectHelper helper) throws IOException {
this.projectPropertiesSave = new ThreadLocal<Boolean>() {
@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<String, String> defs = new HashMap<String, String>();
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<Boolean>(){
@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<Void>() {
@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<String,Integer> CAT_MAP;
static {
final Map<String,Integer> 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<DataObject> sort(@NonNull final List<DataObject> original) {
if (!isLibrary()) {
return original;
}
final List<DataObject> result = new ArrayList<>(Collections.<DataObject>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<DataObject> filterNulls(@NonNull final List<DataObject> list) {
boolean hasNull = false;
for (int i=0; i<CAT_MAP.size(); i++) {
if (list.get(i) == null) {
hasNull = true;
break;
}
}
if (!hasNull) {
//No copy needed
return list;
}
final List<DataObject> 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<String> 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<FileObject, Reference<StatusImpl>> file2Status = new WeakHashMap<FileObject, Reference<StatusImpl>>();
@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<StatusImpl> 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<StatusImpl>(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<Reference<StatusImpl>> toRefresh;
synchronized (this) {
toRefresh = new LinkedList<Reference<StatusImpl>>(file2Status.values());
}
for (Reference<StatusImpl> 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<childNodes.getLength();i++) {
Element oldJaxRpcClient = (Element) childNodes.item(i);
Element newJaxRpcClient = doc.createElementNS(JAX_RPC_NAMESPACE, JAX_RPC_CLIENT);
NodeList nodeProps = oldJaxRpcClient.getChildNodes();
for (int j=0;j<nodeProps.getLength();j++) {
Node n = nodeProps.item(j);
if (n instanceof Element) {
Element oldProp = (Element) n;
Element newProp = doc.createElementNS(JAX_RPC_NAMESPACE, oldProp.getLocalName());
String text = oldProp.getTextContent();
newProp.setTextContent(text);
newJaxRpcClient.appendChild(newProp);
}
}
newJaxRpcClients.appendChild(newJaxRpcClient);
}
aux.putConfigurationFragment(newJaxRpcClients, true);
data.removeChild(oldJaxRpcClients);
helper.putPrimaryConfigurationData(data, true);
}
updateHelper.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH, ep);
}
};
}
@NonNull
private Runnable newMissingPropertiesAction() {
return new Runnable() {
@Override
public void run() {
final EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
if (!ep.containsKey(ProjectProperties.INCLUDES)) {
ep.setProperty(ProjectProperties.INCLUDES, "**"); // NOI18N
}
if (!ep.containsKey(ProjectProperties.EXCLUDES)) {
ep.setProperty(ProjectProperties.EXCLUDES, ""); // NOI18N
}
if (!ep.containsKey("build.generated.sources.dir")) { // NOI18N
ep.setProperty("build.generated.sources.dir", "${build.dir}/generated-sources"); // NOI18N
}
J2SEProjectBuilder.createDefaultModuleProperties(
ep,
testRoots.getRoots().length > 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<URL> 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<File, Project> oldProject) {
switch (operation) {
case COPY:
projectUpdate.setTransparentUpdate(true);
case MOVE:
case RENAME:
project.setName(newName);
}
}
};
}
}