org.netbeans.modules.php.project.PhpProject Maven / Gradle / Ivy
/*
* 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.php.project;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.StaticResource;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectInformation;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.queries.VisibilityQuery;
import org.netbeans.api.search.SearchRoot;
import org.netbeans.api.search.SearchScopeOptions;
import org.netbeans.api.search.provider.SearchInfo;
import org.netbeans.api.search.provider.SearchInfoUtils;
import org.netbeans.api.search.provider.SearchListener;
import org.netbeans.modules.php.api.documentation.PhpDocumentations;
import org.netbeans.modules.php.api.phpmodule.PhpModule;
import org.netbeans.modules.php.api.util.FileUtils;
import org.netbeans.modules.php.project.annotations.ProjectUserAnnotationsProvider;
import org.netbeans.modules.php.project.api.PhpSeleniumProvider;
import org.netbeans.modules.php.project.api.PhpSourcePath;
import org.netbeans.modules.php.project.classpath.BasePathSupport;
import org.netbeans.modules.php.project.classpath.ClassPathProviderImpl;
import org.netbeans.modules.php.project.classpath.IncludePathClassPathProvider;
import org.netbeans.modules.php.project.copysupport.CopySupport;
import org.netbeans.modules.php.project.internalserver.InternalWebServer;
import org.netbeans.modules.php.project.problems.ProjectPropertiesProblemProvider;
import org.netbeans.modules.php.project.ui.actions.support.CommandUtils;
import org.netbeans.modules.php.project.ui.actions.support.ConfigAction;
import org.netbeans.modules.php.project.ui.actions.support.DebugStarterFactory;
import org.netbeans.modules.php.project.ui.codecoverage.PhpCoverageProvider;
import org.netbeans.modules.php.project.ui.customizer.CompositePanelProviderImpl;
import org.netbeans.modules.php.project.ui.customizer.CustomizerProviderImpl;
import org.netbeans.modules.php.project.ui.customizer.IgnorePathSupport;
import org.netbeans.modules.php.project.ui.customizer.PhpProjectProperties;
import org.netbeans.modules.php.project.ui.logicalview.PhpLogicalViewProvider;
import org.netbeans.modules.php.project.ui.options.PhpOptions;
import org.netbeans.modules.php.project.util.PhpProjectUtils;
import org.netbeans.modules.php.project.util.UsageLogging;
import org.netbeans.modules.php.spi.executable.DebugStarter;
import org.netbeans.modules.php.spi.framework.PhpFrameworkProvider;
import org.netbeans.modules.php.spi.framework.PhpModuleIgnoredFilesExtender;
import org.netbeans.modules.php.spi.testing.PhpTestingProvider;
import org.netbeans.modules.php.spi.testing.PhpTestingProviders;
import org.netbeans.modules.web.browser.api.BrowserSupport;
import org.netbeans.modules.web.browser.api.WebBrowser;
import org.netbeans.modules.web.browser.api.BrowserUISupport;
import org.netbeans.modules.web.browser.spi.PageInspectorCustomizer;
import org.netbeans.modules.web.clientproject.api.jstesting.JsTestingProvider;
import org.netbeans.modules.web.clientproject.api.jstesting.JsTestingProviders;
import org.netbeans.modules.web.common.api.CssPreprocessor;
import org.netbeans.modules.web.common.api.CssPreprocessors;
import org.netbeans.modules.web.common.api.CssPreprocessorsListener;
import org.netbeans.modules.web.common.api.UsageLogger;
import org.netbeans.modules.web.common.api.WebUtils;
import org.netbeans.modules.web.common.spi.ProjectWebRootProvider;
import org.netbeans.modules.web.common.spi.ServerURLMappingImplementation;
import org.netbeans.modules.web.common.ui.api.CssPreprocessorsUI;
import org.netbeans.spi.project.AuxiliaryConfiguration;
import org.netbeans.spi.project.support.LookupProviderSupport;
import org.netbeans.spi.project.support.ant.AntBasedProjectRegistration;
import org.netbeans.spi.project.support.ant.AntProjectEvent;
import org.netbeans.spi.project.support.ant.AntProjectHelper;
import org.netbeans.spi.project.support.ant.AntProjectListener;
import org.netbeans.spi.project.support.ant.EditableProperties;
import org.netbeans.spi.project.support.ant.FilterPropertyProvider;
import org.netbeans.spi.project.support.ant.ProjectXmlSavedHook;
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.ProjectOpenedHook;
import org.netbeans.spi.project.ui.support.UILookupMergerSupport;
import org.netbeans.spi.search.SearchFilterDefinition;
import org.netbeans.spi.search.SearchInfoDefinition;
import org.netbeans.spi.search.SearchInfoDefinitionFactory;
import org.netbeans.spi.search.SubTreeSearchOptions;
import org.openide.awt.HtmlBrowser;
import org.openide.awt.NotificationDisplayer;
import org.openide.filesystems.FileAttributeEvent;
import org.openide.filesystems.FileChangeListener;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileRenameEvent;
import org.openide.filesystems.FileUtil;
import org.openide.util.ChangeSupport;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.WeakListeners;
import org.openide.util.WeakSet;
import org.openide.util.lookup.Lookups;
import org.openide.windows.WindowManager;
import org.openide.windows.WindowSystemEvent;
import org.openide.windows.WindowSystemListener;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
* @author ads, Tomas Mysik
*/
@AntBasedProjectRegistration(
type=PhpProjectType.TYPE,
iconResource=PhpProject.PROJECT_ICON,
sharedNamespace=PhpProjectType.PROJECT_CONFIGURATION_NAMESPACE,
privateNamespace=PhpProjectType.PRIVATE_CONFIGURATION_NAMESPACE
)
public final class PhpProject implements Project {
static final Logger LOGGER = Logger.getLogger(PhpProject.class.getName());
@StaticResource
public static final String PROJECT_ICON = "org/netbeans/modules/php/project/ui/resources/phpProject.png"; // NOI18N
final AntProjectHelper helper;
final UpdateHelper updateHelper;
private final ReferenceHelper refHelper;
private final PropertyEvaluator eval;
private final Lookup lookup;
private final SourceRoots sourceRoots;
private final SourceRoots testRoots;
private final SourceRoots seleniumRoots;
private final SearchFilterDefinition searchFilterDef = new PhpSearchFilterDef();
// ok to read it more times
volatile FileObject webRootDirectory;
volatile String name;
private final AntProjectListener phpAntProjectListener = new PhpAntProjectListener();
private final PropertyChangeListener projectPropertiesListener = new ProjectPropertiesListener();
// @GuardedBy("ProjectManager.mutex() & ignoredFoldersLock") #211924
Set ignoredFolders;
final Object ignoredFoldersLock = new Object();
// changes in ignored files - special case because of PhpVisibilityQuery
final ChangeSupport ignoredFoldersChangeSupport = new ChangeSupport(this);
final Frameworks frameworks;
final TestingProviders testingProviders;
private final ChangeListener frameworksListener;
// FS changes
private final SourceDirectoryFileChangeListener sourceDirectoryFileChangeListener = new SourceDirectoryFileChangeListener();
// project's property changes
public static final String PROP_FRAMEWORKS = "frameworks"; // NOI18N
public static final String PROP_WEB_ROOT = "webRoot"; // NOI18N
final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private final Set propertyChangeListeners = new WeakSet<>();
// css preprocessors
final CssPreprocessorsListener cssPreprocessorsListener = new CssPreprocessorsListener() {
@Override
public void preprocessorsChanged() {
// noop?
}
@Override
public void optionsChanged(CssPreprocessor cssPreprocessor) {
recompileSources(cssPreprocessor);
}
@Override
public void customizerChanged(Project project, CssPreprocessor cssPreprocessor) {
if (project.equals(PhpProject.this)) {
recompileSources(cssPreprocessor);
}
}
@Override
public void processingErrorOccured(Project project, CssPreprocessor cssPreprocessor, String error) {
// noop
}
};
// #233052
private final WindowSystemListener windowSystemListener = new WindowSystemListener() {
@Override
public void beforeLoad(WindowSystemEvent event) {
}
@Override
public void afterLoad(WindowSystemEvent event) {
}
@Override
public void beforeSave(WindowSystemEvent event) {
// browser
getLookup().lookup(ClientSideDevelopmentSupport.class).close();
}
@Override
public void afterSave(WindowSystemEvent event) {
}
};
@NbBundle.Messages({
"PhpProject.sourceRoots.sources=Source Files",
"PhpProject.sourceRoots.tests=Test Files",
"PhpProject.sourceRoots.selenium=Selenium Test Files",
})
public PhpProject(AntProjectHelper helper) {
assert helper != null;
this.helper = helper;
updateHelper = new UpdateHelper(UpdateImplementation.NULL, helper);
AuxiliaryConfiguration configuration = helper.createAuxiliaryConfiguration();
eval = createEvaluator();
refHelper = new ReferenceHelper(helper, configuration, getEvaluator());
sourceRoots = SourceRoots.Builder.create(updateHelper, eval, Bundle.PhpProject_sourceRoots_sources())
.setProperties(PhpProjectProperties.SRC_DIR)
.build();
testRoots = SourceRoots.Builder.create(updateHelper, eval, Bundle.PhpProject_sourceRoots_tests())
.setPropertyNumericPrefix(PhpProjectProperties.TEST_SRC_DIR)
.setTests(true)
.build();
seleniumRoots = SourceRoots.Builder.create(updateHelper, eval, Bundle.PhpProject_sourceRoots_selenium())
.setPropertyNumericPrefix(PhpProjectProperties.SELENIUM_SRC_DIR)
.setTests(true)
.build();
PhpModuleImpl phpModule = new PhpModuleImpl(this);
frameworks = new Frameworks(phpModule);
testingProviders = TestingProviders.create(this);
// lookup
lookup = createLookup(configuration, phpModule);
// listeners
addWeakPropertyEvaluatorListener(projectPropertiesListener);
helper.addAntProjectListener(WeakListeners.create(AntProjectListener.class, phpAntProjectListener, helper));
sourceRoots.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
removeSourceDirListener();
addSourceDirListener();
resetFrameworks();
}
});
frameworksListener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
fireFrameworksChange();
}
};
frameworks.addChangeListener(WeakListeners.change(frameworksListener, frameworks));
WindowManager windowManager = WindowManager.getDefault();
windowManager.addWindowSystemListener(WeakListeners.create(WindowSystemListener.class, windowSystemListener, windowManager));
}
@Override
public Lookup getLookup() {
return lookup;
}
PropertyEvaluator getEvaluator() {
return eval;
}
void addWeakPropertyEvaluatorListener(PropertyChangeListener listener) {
eval.addPropertyChangeListener(WeakListeners.propertyChange(listener, eval));
}
void addWeakIgnoredFilesListener(ChangeListener listener) {
ignoredFoldersChangeSupport.addChangeListener(WeakListeners.change(listener, ignoredFoldersChangeSupport));
VisibilityQuery visibilityQuery = VisibilityQuery.getDefault();
visibilityQuery.addChangeListener(WeakListeners.change(listener, visibilityQuery));
}
// add as a weak listener, only once
boolean addWeakPropertyChangeListener(PropertyChangeListener listener) {
if (!propertyChangeListeners.add(listener)) {
// already added
return false;
}
addPropertyChangeListener(WeakListeners.propertyChange(listener, propertyChangeSupport));
return true;
}
void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
public SearchFilterDefinition getSearchFilterDefinition() {
return searchFilterDef;
}
private PropertyEvaluator createEvaluator() {
// It is currently safe to not use the UpdateHelper for PropertyEvaluator; UH.getProperties() delegates to APH
// Adapted from APH.getStandardPropertyEvaluator (delegates to ProjectProperties):
PropertyEvaluator baseEval1 = PropertyUtils.sequentialPropertyEvaluator(
helper.getStockPropertyPreprovider(),
helper.getPropertyProvider(PhpConfigurationProvider.CONFIG_PROPS_PATH));
PropertyEvaluator baseEval2 = PropertyUtils.sequentialPropertyEvaluator(
helper.getStockPropertyPreprovider(),
helper.getPropertyProvider(AntProjectHelper.PRIVATE_PROPERTIES_PATH));
return PropertyUtils.sequentialPropertyEvaluator(
helper.getStockPropertyPreprovider(),
helper.getPropertyProvider(PhpConfigurationProvider.CONFIG_PROPS_PATH),
new ConfigPropertyProvider(baseEval1, "nbproject/private/configs", helper), // NOI18N
helper.getPropertyProvider(AntProjectHelper.PRIVATE_PROPERTIES_PATH),
helper.getProjectLibrariesPropertyProvider(),
PropertyUtils.userPropertiesProvider(baseEval2,
"user.properties.file", FileUtil.toFile(getProjectDirectory())), // NOI18N
new ConfigPropertyProvider(baseEval1, "nbproject/configs", helper), // NOI18N
helper.getPropertyProvider(AntProjectHelper.PROJECT_PROPERTIES_PATH));
}
@Override
public FileObject getProjectDirectory() {
return getHelper().getProjectDirectory();
}
public SourceRoots getSourceRoots() {
return sourceRoots;
}
public SourceRoots getTestRoots() {
return testRoots;
}
public SourceRoots getSeleniumRoots() {
return seleniumRoots;
}
@CheckForNull
FileObject getSourcesDirectory() {
for (FileObject root : sourceRoots.getRoots()) {
// return the first one
return root;
}
return null;
}
/**
* @return tests directory or null
*/
FileObject[] getTestsDirectories() {
return testRoots.getRoots();
}
/**
* @return selenium tests directory or null
*/
FileObject[] getSeleniumDirectories() {
return seleniumRoots.getRoots();
}
/**
* @return selenium tests directory or null
*/
FileObject getSeleniumDirectory() {
for (FileObject root : seleniumRoots.getRoots()) {
// return the first one
return root;
}
return null;
}
/**
* @return web root directory or sources directory if not set
*/
FileObject getWebRootDirectory() {
if (webRootDirectory == null) {
webRootDirectory = resolveWebRootDirectory();
}
return webRootDirectory;
}
private FileObject resolveWebRootDirectory() {
if (PhpProjectValidator.isFatallyBroken(this)) {
// corrupted project
return null;
}
FileObject sources = getSourcesDirectory();
assert sources != null;
String webRootProperty = eval.getProperty(PhpProjectProperties.WEB_ROOT);
if (webRootProperty == null) {
// web root directory not set, return sources
return sources;
}
FileObject webRootDir = sources.getFileObject(webRootProperty);
if (webRootDir != null) {
return webRootDir;
}
LOGGER.log(Level.INFO, "Web root directory {0} not found for project {1}", new Object[] {webRootProperty, getName()});
// web root directory not found, return sources
return sources;
}
void addSourceDirListener() {
FileObject sourcesDirectory = getSourcesDirectory();
if (sourcesDirectory == null) {
return;
}
if (sourcesDirectory.equals(sourceDirectoryFileChangeListener.getSourceDir())) {
// already listening to this source dir
// this usually happens for new project - property change is fired _before_ project open
return;
}
synchronized (sourceDirectoryFileChangeListener) {
sourceDirectoryFileChangeListener.setSourceDir(sourcesDirectory);
FileUtil.addRecursiveListener(sourceDirectoryFileChangeListener, FileUtil.toFile(sourcesDirectory), new FileFilter() {
@Override
public boolean accept(File pathname) {
return isVisible(pathname);
}
}, null);
}
}
void removeSourceDirListener() {
FileObject sourceDir = sourceDirectoryFileChangeListener.getSourceDir();
if (sourceDir == null) {
// not listening
return;
}
synchronized (sourceDirectoryFileChangeListener) {
try {
FileUtil.removeRecursiveListener(sourceDirectoryFileChangeListener, FileUtil.toFile(sourceDir));
} catch (IllegalArgumentException ex) {
LOGGER.log(Level.INFO, null, ex);
} finally {
sourceDirectoryFileChangeListener.setSourceDir(null);
}
}
}
void recompileSources(CssPreprocessor cssPreprocessor) {
assert cssPreprocessor != null;
FileObject sourcesDirectory = getSourcesDirectory();
if (sourcesDirectory == null) {
return;
}
// force recompiling
CssPreprocessors.getDefault().process(cssPreprocessor, this, sourcesDirectory);
}
public PhpModule getPhpModule() {
PhpModule phpModule = getLookup().lookup(PhpModuleImpl.class);
assert phpModule != null;
return phpModule;
}
boolean isVisible(File file) {
if (getIgnoredFiles().contains(file)) {
return false;
}
return VisibilityQuery.getDefault().isVisible(file);
}
boolean isVisible(FileObject fileObject) {
File file = FileUtil.toFile(fileObject);
if (file == null) {
if (getIgnoredFileObjects().contains(fileObject)) {
return false;
}
return VisibilityQuery.getDefault().isVisible(fileObject);
}
return isVisible(file);
}
public Set getIgnoredFiles() {
Set ignored = new HashSet<>();
addIgnoredProjectFiles(ignored);
addIgnoredFrameworkFiles(ignored);
return ignored;
}
// #172139 caused NPE in GlobalVisibilityQueryImpl
public Set getIgnoredFileObjects() {
Set ignoredFileObjects = new HashSet<>();
for (File file : getIgnoredFiles()) {
FileObject fo = FileUtil.toFileObject(file);
if (fo != null) {
ignoredFileObjects.add(fo);
}
}
return ignoredFileObjects;
}
private void addIgnoredProjectFiles(final Set ignored) {
ProjectManager.mutex().readAccess(new Mutex.Action() {
@Override
public Void run() {
synchronized (ignoredFoldersLock) {
if (ignoredFolders == null) {
ignoredFolders = resolveIgnoredFolders(PhpProjectProperties.IGNORE_PATH);
}
assert ignoredFolders != null : "Ignored folders cannot be null";
for (BasePathSupport.Item item : ignoredFolders) {
if (item.isBroken()) {
continue;
}
ignored.add(new File(item.getAbsoluteFilePath(helper.getProjectDirectory())));
}
}
return null;
}
});
}
private void resetIgnoredFolders() {
ProjectManager.mutex().readAccess(new Mutex.Action() {
@Override
public Void run() {
synchronized (ignoredFoldersLock) {
ignoredFolders = null;
}
return null;
}
});
}
private void addIgnoredFrameworkFiles(Set ignored) {
PhpModule phpModule = getPhpModule();
for (PhpFrameworkProvider provider : getFrameworks()) {
PhpModuleIgnoredFilesExtender ignoredFilesExtender = provider.getIgnoredFilesExtender(phpModule);
if (ignoredFilesExtender == null) {
continue;
}
for (File file : ignoredFilesExtender.getIgnoredFiles()) {
assert file != null : "Ignored file = null found in " + provider.getIdentifier();
assert file.isAbsolute() : "Not absolute file found in " + provider.getIdentifier();
ignored.add(file);
}
}
}
// no need to cache it, it is called only for code analysis
public Set getCodeAnalysisExcludeFileObjects() {
Set excludedFileObjects = new HashSet<>();
Set excluded = resolveIgnoredFolders(PhpProjectProperties.CODE_ANALYSIS_EXCLUDES);
assert excluded != null : "Ignored folders cannot be null";
for (BasePathSupport.Item item : excluded) {
if (item.isBroken()) {
continue;
}
File file = new File(item.getAbsoluteFilePath(helper.getProjectDirectory()));
FileObject fo = FileUtil.toFileObject(file);
if (fo != null) {
excludedFileObjects.add(fo);
}
}
return excludedFileObjects;
}
private Set resolveIgnoredFolders(String propertyName) {
IgnorePathSupport ignorePathSupport = new IgnorePathSupport(eval, refHelper, helper);
Set ignored = new HashSet<>();
EditableProperties properties = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
Iterator itemsIterator = ignorePathSupport.itemsIterator(properties.getProperty(propertyName));
while (itemsIterator.hasNext()) {
ignored.add(itemsIterator.next());
}
return ignored;
}
public List getFrameworks() {
return frameworks.getFrameworks();
}
public void resetFrameworks() {
List oldFrameworkProviders = getFrameworks();
frameworks.resetFrameworks();
List newFrameworkProviders = getFrameworks();
if (!oldFrameworkProviders.equals(newFrameworkProviders)) {
fireFrameworksChange();
}
}
void fireFrameworksChange() {
propertyChangeSupport.firePropertyChange(PROP_FRAMEWORKS, null, null);
// #209206 - also, likely some files are newly hidden/visible
fireIgnoredFilesChange();
}
public List getTestingProviders() {
return testingProviders.getTestingProviders();
}
public String getName() {
if (name == null) {
ProjectManager.mutex().readAccess(new Mutex.Action() {
@Override
public Void run() {
Element data = getHelper().getPrimaryConfigurationData(true);
NodeList nl = data.getElementsByTagNameNS(PhpProjectType.PROJECT_CONFIGURATION_NAMESPACE, "name"); // NOI18N
if (nl.getLength() == 1) {
nl = nl.item(0).getChildNodes();
if (nl.getLength() == 1
&& nl.item(0).getNodeType() == Node.TEXT_NODE) {
name = ((Text) nl.item(0)).getNodeValue();
}
}
if (name == null) {
name = "???"; // NOI18N
}
return null;
}
});
}
assert name != null;
return name;
}
public void setName(final String name) {
ProjectManager.mutex().writeAccess(new Runnable() {
@Override
public void run() {
Element data = getHelper().getPrimaryConfigurationData(true);
NodeList nl = data.getElementsByTagNameNS(PhpProjectType.PROJECT_CONFIGURATION_NAMESPACE, "name"); // NOI18N
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(
PhpProjectType.PROJECT_CONFIGURATION_NAMESPACE, "name"); // NOI18N
data.insertBefore(nameEl, /* OK if null */data.getChildNodes().item(0));
}
nameEl.appendChild(data.getOwnerDocument().createTextNode(name));
getHelper().putPrimaryConfigurationData(data, true);
}
});
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder(200);
buffer.append(getClass().getName());
buffer.append(" [ project directory: ");
buffer.append(getProjectDirectory());
buffer.append(" ]");
return buffer.toString();
}
public AntProjectHelper getHelper() {
return helper;
}
public CopySupport getCopySupport() {
return getLookup().lookup(CopySupport.class);
}
private Lookup createLookup(AuxiliaryConfiguration configuration, PhpModule phpModule) {
PhpProjectEncodingQueryImpl phpProjectEncodingQueryImpl = new PhpProjectEncodingQueryImpl(getEvaluator());
Lookup base = Lookups.fixed(new Object[] {
this,
CopySupport.getInstance(this),
new SeleniumProvider(),
new PhpCoverageProvider(this),
new Info(),
configuration,
new PhpOpenedHook(),
new PhpProjectXmlSavedHook(),
new PhpActionProvider(this),
new PhpConfigurationProvider(this),
phpModule,
PhpLanguagePropertiesAccessor.getDefault().createForProject(this),
new PhpEditorExtender(this),
helper.createCacheDirectoryProvider(),
helper.createAuxiliaryProperties(),
new ClassPathProviderImpl(this, getSourceRoots(), getTestRoots(), getSeleniumRoots()),
new PhpLogicalViewProvider(this),
new CustomizerProviderImpl(this),
PhpSharabilityQuery.create(helper, getEvaluator(), getSourceRoots(), getTestRoots(), getSeleniumRoots()),
LookupProviderSupport.createSharabilityQueryMerger(),
new PhpProjectOperations(this) ,
phpProjectEncodingQueryImpl,
new CreateFromTemplateAttributesImpl(getHelper(), phpProjectEncodingQueryImpl),
new PhpTemplates(),
new PhpSources(this, getHelper(), getEvaluator(), getSourceRoots(), getTestRoots(), getSeleniumRoots()),
getHelper(),
getEvaluator(),
PhpSearchInfo.create(this),
new PhpSubTreeSearchOptions(),
new PhpTestingProvidersImpl(testingProviders),
InternalWebServer.createForProject(this),
CssPreprocessorsUI.getDefault().createProjectProblemsProvider(this),
ProjectPropertiesProblemProvider.createForProject(this),
UILookupMergerSupport.createProjectProblemsProviderMerger(),
new ProjectWebRootProviderImpl(),
ClientSideDevelopmentSupport.create(this),
ProjectBrowserProviderImpl.create(this),
new PhpVisibilityQuery.PhpVisibilityQueryImpl(this),
new UsageLogging(),
new ImportantFilesImpl(this),
new ProjectUserAnnotationsProvider(this),
// ?? getRefHelper()
});
return LookupProviderSupport.createCompositeLookup(base, "Projects/org-netbeans-modules-php-project/Lookup"); // NOI18N
}
public ReferenceHelper getRefHelper() {
return refHelper;
}
public void fireIgnoredFilesChange() {
resetIgnoredFolders();
ignoredFoldersChangeSupport.fireChange();
}
private final class Info implements ProjectInformation {
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
@Override
public String getDisplayName() {
return PhpProject.this.getName();
}
@Override
public Icon getIcon() {
return ImageUtilities.image2Icon(ImageUtilities.loadImage(PROJECT_ICON));
}
@Override
public String getName() {
return PropertyUtils.getUsablePropertyName(getDisplayName());
}
@Override
public Project getProject() {
return PhpProject.this;
}
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
void firePropertyChange(String prop) {
propertyChangeSupport.firePropertyChange(prop , null, null);
}
}
private final class PhpOpenedHook extends ProjectOpenedHook {
@Override
protected void projectOpened() {
new ProjectUpgrader(PhpProject.this).upgrade();
addSourceDirListener();
CssPreprocessors.getDefault().addCssPreprocessorsListener(cssPreprocessorsListener);
frameworks.projectOpened();
// avoid slowness
readFolders();
getTestingProviders();
PhpDocumentations.getDocumentations();
getName();
PhpOptions.getInstance().ensurePhpGlobalIncludePath();
ClassPathProviderImpl cpProvider = lookup.lookup(ClassPathProviderImpl.class);
ClassPath[] bootClassPaths = cpProvider.getProjectClassPaths(PhpSourcePath.BOOT_CP);
GlobalPathRegistry.getDefault().register(PhpSourcePath.BOOT_CP, bootClassPaths);
GlobalPathRegistry.getDefault().register(PhpSourcePath.PROJECT_BOOT_CP, cpProvider.getProjectClassPaths(PhpSourcePath.PROJECT_BOOT_CP));
GlobalPathRegistry.getDefault().register(PhpSourcePath.SOURCE_CP, cpProvider.getProjectClassPaths(PhpSourcePath.SOURCE_CP));
for (ClassPath classPath : bootClassPaths) {
IncludePathClassPathProvider.addProjectIncludePath(classPath);
}
// ensure that code coverage is initialized in case it's enabled...
PhpCoverageProvider coverageProvider = getLookup().lookup(PhpCoverageProvider.class);
if (coverageProvider.isEnabled()) {
PhpCoverageProvider.notifyProjectOpened(PhpProject.this);
}
JsTestingProvider jsTestingProvider = JsTestingProviders.getDefault().getJsTestingProvider(PhpProject.this, false);
if (jsTestingProvider != null) {
jsTestingProvider.projectOpened(PhpProject.this);
}
// autoconfigured?
checkAutoconfigured();
// #187060 - exception in projectOpened => project IS NOT opened (so move it at the end of the hook)
getCopySupport().projectOpened();
// log usage
PhpProjectUtils.logUsage(PhpProject.class, "USG_PROJECT_OPEN_PHP", Arrays.asList(PhpProjectUtils.getFrameworksForUsage(frameworks.getFrameworks()))); // NOI18N
// #192386
LOGGER.finest("PROJECT_OPENED_FINISHED");
}
@Override
protected void projectClosed() {
try {
removeSourceDirListener();
CssPreprocessors.getDefault().removeCssPreprocessorsListener(cssPreprocessorsListener);
frameworks.projectClosed();
ClassPathProviderImpl cpProvider = lookup.lookup(ClassPathProviderImpl.class);
ClassPath[] bootClassPaths = cpProvider.getProjectClassPaths(PhpSourcePath.BOOT_CP);
GlobalPathRegistry.getDefault().unregister(PhpSourcePath.BOOT_CP, bootClassPaths);
GlobalPathRegistry.getDefault().unregister(PhpSourcePath.PROJECT_BOOT_CP, cpProvider.getProjectClassPaths(PhpSourcePath.PROJECT_BOOT_CP));
GlobalPathRegistry.getDefault().unregister(PhpSourcePath.SOURCE_CP, cpProvider.getProjectClassPaths(PhpSourcePath.SOURCE_CP));
for (ClassPath classPath : bootClassPaths) {
IncludePathClassPathProvider.removeProjectIncludePath(classPath);
}
// internal web server
lookup.lookup(InternalWebServer.class).stop();
// browser
lookup.lookup(ClientSideDevelopmentSupport.class).close();
JsTestingProvider jsTestingProvider = JsTestingProviders.getDefault().getJsTestingProvider(PhpProject.this, false);
if (jsTestingProvider != null) {
jsTestingProvider.projectClosed(PhpProject.this);
}
} finally {
// #187060 - exception in projectClosed => project IS closed (so do it in finally block)
getCopySupport().projectClosed();
// #192386
LOGGER.finest("PROJECT_CLOSED_FINISHED");
}
}
private void readFolders() {
// #165494 - moved from projectClosed() to projectOpened()
// clear references to ensure that all the dirs are read again
webRootDirectory = null;
resetIgnoredFolders();
// read dirs
getIgnoredFiles();
getSourceRoots().getRoots();
getTestRoots().getRoots();
getSeleniumRoots().getRoots();
}
@NbBundle.Messages({
"# {0} - project name",
"PhpOpenedHook.notification.autoconfigured.title=Project {0} automatically configured",
"PhpOpenedHook.notification.autoconfigured.details=Review and correct important project settings detected by the IDE.",
})
private void checkAutoconfigured() {
PhpProjectProperties projectProperties = new PhpProjectProperties(PhpProject.this);
if (projectProperties.isAutoconfigured()) {
NotificationDisplayer.getDefault().notify(
Bundle.PhpOpenedHook_notification_autoconfigured_title(ProjectUtils.getInformation(PhpProject.this).getDisplayName()),
NotificationDisplayer.Priority.LOW.getIcon(),
Bundle.PhpOpenedHook_notification_autoconfigured_details(),
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PhpProjectUtils.openCustomizer(PhpProject.this, CompositePanelProviderImpl.SOURCES);
}
},
NotificationDisplayer.Priority.LOW);
projectProperties.setAutoconfigured(false);
projectProperties.save();
}
}
}
private static final class ConfigPropertyProvider extends FilterPropertyProvider implements PropertyChangeListener {
private final PropertyEvaluator baseEval;
private final String prefix;
private final AntProjectHelper helper;
public ConfigPropertyProvider(PropertyEvaluator baseEval, String prefix, AntProjectHelper helper) {
super(computeDelegate(baseEval, prefix, helper));
this.baseEval = baseEval;
this.prefix = prefix;
this.helper = helper;
baseEval.addPropertyChangeListener(this);
}
@Override
public void propertyChange(PropertyChangeEvent ev) {
if (PhpConfigurationProvider.PROP_CONFIG.equals(ev.getPropertyName())) {
setDelegate(computeDelegate(baseEval, prefix, helper));
}
}
private static PropertyProvider computeDelegate(PropertyEvaluator baseEval, String prefix, AntProjectHelper helper) {
String config = baseEval.getProperty(PhpConfigurationProvider.PROP_CONFIG);
if (config != null) {
return helper.getPropertyProvider(prefix + "/" + config + ".properties"); // NOI18N
}
return PropertyUtils.fixedPropertyProvider(Collections.emptyMap());
}
}
public final class PhpProjectXmlSavedHook extends ProjectXmlSavedHook {
@Override
protected void projectXmlSaved() throws IOException {
Info info = getLookup().lookup(Info.class);
assert info != null;
info.firePropertyChange(ProjectInformation.PROP_NAME);
info.firePropertyChange(ProjectInformation.PROP_DISPLAY_NAME);
}
}
private final class SeleniumProvider implements PhpSeleniumProvider {
@Override
public FileObject getTestDirectory(boolean showCustomizer) {
return ProjectPropertiesSupport.getSeleniumDirectory(PhpProject.this, showCustomizer);
}
@Override
public void runAllTests() {
ConfigAction.get(ConfigAction.Type.SELENIUM, PhpProject.this).runProject();
}
@Override
public boolean isSupportEnabled(FileObject[] activatedFOs) {
if (activatedFOs.length == 0) {
return false;
}
PhpProject onlyOneProjectAllowed = null;
for (FileObject fileObj : activatedFOs) {
if (fileObj == null) {
return false;
}
// only php files or folders allowed
if (fileObj.isData() && !FileUtils.isPhpFile(fileObj)) {
return false;
}
PhpProject phpProject = PhpProjectUtils.getPhpProject(fileObj);
if (phpProject == null) {
return false;
}
if (PhpProjectValidator.isFatallyBroken(phpProject)) {
return false;
}
if (onlyOneProjectAllowed == null) {
onlyOneProjectAllowed = phpProject;
} else {
if (!onlyOneProjectAllowed.equals(phpProject)) {
// tests can be generated only for one project at one time
return false;
}
}
if (fileObj == phpProject.getProjectDirectory()) { // "Run Selenium Tests" action should be active for the project node
return true;
}
FileObject sources = ProjectPropertiesSupport.getSourcesDirectory(phpProject);
if (sources == null || sources.equals(fileObj)) {
return false;
}
if (!CommandUtils.isUnderSources(phpProject, fileObj)
|| CommandUtils.isUnderTests(phpProject, fileObj, false)
|| CommandUtils.isUnderSelenium(phpProject, fileObj, false)) {
return false;
}
}
return true;
}
@Override
public List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy