blob: b778d526944826caa951c9ab138a15229e0baff0 [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.php.project;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.event.ChangeListener;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.progress.BaseProgressUtils;
import org.netbeans.modules.php.api.PhpVersion;
import org.netbeans.modules.php.api.executable.InvalidPhpExecutableException;
import org.netbeans.modules.php.api.executable.PhpInterpreter;
import org.netbeans.modules.php.api.util.StringUtils;
import org.netbeans.modules.php.project.api.PhpLanguageProperties;
import org.netbeans.modules.php.project.api.PhpOptions;
import org.netbeans.modules.php.project.ui.BrowseTestSources;
import org.netbeans.modules.php.project.ui.customizer.PhpProjectProperties;
import org.netbeans.modules.web.browser.api.WebBrowser;
import org.netbeans.modules.web.browser.api.BrowserUISupport;
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
import org.netbeans.spi.project.support.ant.PropertyUtils;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
import org.openide.util.Pair;
/**
* Helper class for getting <b>all</b> the properties of a PHP project.
* <p>
* <b>This class is the preferred way to get PHP project properties.</b>
* @author Tomas Mysik
*/
public final class ProjectPropertiesSupport {
private ProjectPropertiesSupport() {
}
// XXX use it everywhere!
/**
* Produce a machine-independent relativized version of a filename from a dir.
* If path cannot be relative, the full path of the given file is returned.
* @param dir base directory
* @param file file to be relativized
* @return relativized version of a filename from a dir or full path of the given file if the path cannot be relativized
* @see PropertyUtils#relativizeFile(File, File)
*/
public static String relativizeFile(File dir, File file) {
String relativePath = PropertyUtils.relativizeFile(dir, file);
if (relativePath == null) {
// path cannot be relativized => use absolute path (any VCS can be hardly use, of course)
relativePath = file.getAbsolutePath();
}
return relativePath;
}
/**
* <b>This method should not be used, use other methods in this class.</b>
* <p>
* Use this method only if you don't want to show customizer automatically
* or if you understand what you are doing ;)
* @see #addWeakPropertyEvaluatorListener(org.netbeans.modules.php.project.PhpProject, java.beans.PropertyChangeListener)
*/
public static PropertyEvaluator getPropertyEvaluator(PhpProject project) {
return project.getEvaluator();
}
public static void addWeakPropertyEvaluatorListener(PhpProject project, PropertyChangeListener listener) {
project.addWeakPropertyEvaluatorListener(listener);
}
public static void addWeakIgnoredFilesListener(PhpProject project, ChangeListener listener) {
project.addWeakIgnoredFilesListener(listener);
}
public static boolean addWeakProjectPropertyChangeListener(PhpProject project, PropertyChangeListener listener) {
return project.addWeakPropertyChangeListener(listener);
}
public static void addProjectPropertyChangeListener(PhpProject project, PropertyChangeListener listener) {
project.addPropertyChangeListener(listener);
}
public static void removeProjectPropertyChangeListener(PhpProject project, PropertyChangeListener listener) {
project.removePropertyChangeListener(listener);
}
public static FileObject getProjectDirectory(PhpProject project) {
return project.getProjectDirectory();
}
@CheckForNull
public static FileObject getSourcesDirectory(PhpProject project) {
return project.getSourcesDirectory();
}
@NbBundle.Messages({
"ProjectPropertiesSupport.browse.tests=Select a directory with project test files.",
"ProjectPropertiesSupport.browse.tests.info=More directories can be added in Project Properties.",
})
public static List<FileObject> getTestDirectories(final PhpProject project, boolean showFileChooser) {
List<FileObject> testDirs = filterValid(project.getTestsDirectories());
if (!testDirs.isEmpty()) {
return testDirs;
}
if (!showFileChooser) {
return Collections.emptyList();
}
// show ui
BrowseTestSources panel = Mutex.EVENT.readAccess(new Mutex.Action<BrowseTestSources>() {
@Override
public BrowseTestSources run() {
return new BrowseTestSources(project, Bundle.ProjectPropertiesSupport_browse_tests(),
Bundle.ProjectPropertiesSupport_browse_tests_info());
}
});
if (!panel.open()) {
return Collections.emptyList();
}
File tests = new File(panel.getTestSources());
assert tests.isDirectory();
FileObject testsDirectory = FileUtil.toFileObject(tests);
saveTestSources(project, PhpProjectProperties.TEST_SRC_DIR, tests);
return Collections.singletonList(testsDirectory);
}
@CheckForNull
public static FileObject getTestDirectory(PhpProject project, FileObject file, boolean showFileChooser) {
List<FileObject> testDirectories = getTestDirectories(project, showFileChooser);
if (testDirectories.isEmpty()) {
return null;
}
// XXX find closest root
FileObject testsDirectory = findClosestDir(testDirectories, file);
assert testsDirectory != null && testsDirectory.isValid() : testsDirectory;
return testsDirectory;
}
@NbBundle.Messages({
"ProjectPropertiesSupport.browse.selenium.test=Select a directory with project selenium test files.",
})
public static List<FileObject> getSeleniumDirectories(final PhpProject project, boolean showFileChooser) {
List<FileObject> testDirs = filterValid(project.getSeleniumDirectories());
if (!testDirs.isEmpty()) {
return testDirs;
}
if (!showFileChooser) {
return Collections.emptyList();
}
// show ui
BrowseTestSources panel = Mutex.EVENT.readAccess(new Mutex.Action<BrowseTestSources>() {
@Override
public BrowseTestSources run() {
return new BrowseTestSources(project, Bundle.ProjectPropertiesSupport_browse_selenium_test(),
Bundle.ProjectPropertiesSupport_browse_tests_info());
}
});
if (!panel.open()) {
return Collections.emptyList();
}
File tests = new File(panel.getTestSources());
assert tests.isDirectory();
FileObject testsDirectory = FileUtil.toFileObject(tests);
saveTestSources(project, PhpProjectProperties.SELENIUM_SRC_DIR, tests);
return Collections.singletonList(testsDirectory);
}
/**
* @return selenium test sources directory or <code>null</code> (if not set up yet e.g.)
*/
public static FileObject getSeleniumDirectory(final PhpProject project, boolean showFileChooser) {
FileObject seleniumDirectory = project.getSeleniumDirectory();
if (seleniumDirectory != null && seleniumDirectory.isValid()) {
return seleniumDirectory;
}
if (showFileChooser) {
BrowseTestSources panel = Mutex.EVENT.readAccess(new Mutex.Action<BrowseTestSources>() {
@Override
public BrowseTestSources run() {
return new BrowseTestSources(project, NbBundle.getMessage(ProjectPropertiesSupport.class, "LBL_BrowseSelenium"));
}
});
if (panel.open()) {
File selenium = new File(panel.getTestSources());
assert selenium.isDirectory();
seleniumDirectory = FileUtil.toFileObject(selenium);
saveTestSources(project, PhpProjectProperties.SELENIUM_SRC_DIR, selenium);
}
}
return seleniumDirectory;
}
@CheckForNull
public static WebBrowser getWebBrowser(PhpProject project) {
String browserId = project.getEvaluator().getProperty(PhpProjectProperties.BROWSER_ID);
if (browserId == null) {
return null;
}
return BrowserUISupport.getBrowser(browserId);
}
public static boolean getBrowserReloadOnSave(PhpProject project) {
return getBoolean(project, PhpProjectProperties.BROWSER_RELOAD_ON_SAVE, true);
}
public static String getWebRoot(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.WEB_ROOT);
}
public static FileObject getWebRootDirectory(PhpProject project) {
return project.getWebRootDirectory();
}
public static File getSourceSubdirectory(PhpProject project, String subdirectoryPath) {
return getSubdirectory(project, project.getSourcesDirectory(), subdirectoryPath);
}
public static File getSubdirectory(PhpProject project, FileObject rootDirectory, String subdirectoryPath) {
File rootDir = FileUtil.toFile(rootDirectory);
if (!StringUtils.hasText(subdirectoryPath)) {
return rootDir;
}
// first try to resolve fileobject
FileObject fo = rootDirectory.getFileObject(subdirectoryPath);
if (fo != null) {
return FileUtil.toFile(fo);
}
// fallback for OS specific paths (should be changed everywhere, my fault, sorry)
return PropertyUtils.resolveFile(FileUtil.toFile(rootDirectory), subdirectoryPath);
}
public static PhpInterpreter getValidPhpInterpreter(PhpProject project) throws InvalidPhpExecutableException {
String interpreter = project.getEvaluator().getProperty(PhpProjectProperties.INTERPRETER);
if (StringUtils.hasText(interpreter)) {
return PhpInterpreter.getCustom(interpreter);
}
return PhpInterpreter.getDefault();
}
public static String getPhpInterpreter(PhpProject project) {
String interpreter = project.getEvaluator().getProperty(PhpProjectProperties.INTERPRETER);
if (StringUtils.hasText(interpreter)) {
return interpreter;
}
return PhpOptions.getInstance().getPhpInterpreter();
}
public static boolean isCopySourcesEnabled(PhpProject project) {
return getBoolean(project, PhpProjectProperties.COPY_SRC_FILES, false);
}
public static boolean isCopySourcesOnOpen(PhpProject project) {
return getBoolean(project, PhpProjectProperties.COPY_SRC_ON_OPEN, false);
}
/**
* @return file or <code>null</code>.
*/
public static File getCopySourcesTarget(PhpProject project) {
String targetString = project.getEvaluator().getProperty(PhpProjectProperties.COPY_SRC_TARGET);
if (targetString != null && targetString.trim().length() > 0) {
return FileUtil.normalizeFile(new File(targetString));
}
return null;
}
public static String getEncoding(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.SOURCE_ENCODING);
}
public static boolean areShortTagsEnabled(PhpProject project) {
return getBoolean(project, PhpProjectProperties.SHORT_TAGS, PhpLanguageProperties.SHORT_TAGS_ENABLED);
}
public static boolean areAspTagsEnabled(PhpProject project) {
return getBoolean(project, PhpProjectProperties.ASP_TAGS, PhpLanguageProperties.ASP_TAGS_ENABLED);
}
public static PhpVersion getPhpVersion(PhpProject project) {
return getPhpVersion(project.getEvaluator().getProperty(PhpProjectProperties.PHP_VERSION));
}
public static PhpVersion getPhpVersion(String value) {
if (value != null) {
try {
return PhpVersion.valueOf(value);
} catch (Exception iae) {
// ignored
}
}
return PhpVersion.getDefault();
}
/**
* @return run as type, {@link PhpProjectProperties.RunAsType#LOCAL} is the default.
*/
public static PhpProjectProperties.RunAsType getRunAs(PhpProject project) {
PhpProjectProperties.RunAsType runAsType = null;
String runAs = project.getEvaluator().getProperty(PhpProjectProperties.RUN_AS);
if (runAs != null) {
try {
runAsType = PhpProjectProperties.RunAsType.valueOf(runAs);
} catch (Exception iae) {
// ignored
}
}
return runAsType != null ? runAsType : PhpProjectProperties.RunAsType.LOCAL;
}
/**
* @return url or <code>null</code>.
*/
public static String getUrl(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.URL);
}
/**
* @return index file or <code>null</code>.
*/
public static String getIndexFile(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.INDEX_FILE);
}
/**
* @return arguments or <code>null</code>.
*/
public static String getArguments(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.ARGS);
}
/**
* @return PHP arguments or <code>null</code>.
*/
public static String getPhpArguments(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.PHP_ARGS);
}
/**
* @return working directory or <code>null</code>.
*/
public static String getWorkDir(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.WORK_DIR);
}
/**
* @return remote connection (configuration) name or <code>null</code>.
*/
public static String getRemoteConnection(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.REMOTE_CONNECTION);
}
/**
* @return remote (upload) directory or <code>null</code>.
*/
public static String getRemoteDirectory(PhpProject project) {
return project.getEvaluator().getProperty(PhpProjectProperties.REMOTE_DIRECTORY);
}
/**
* @return <code>true</code> if permissions should be preserved; default is <code>false</code>.
*/
public static boolean areRemotePermissionsPreserved(PhpProject project) {
return getBoolean(project, PhpProjectProperties.REMOTE_PERMISSIONS, false);
}
/**
* @return <code>true</code> if upload is direct (and not using a temporary file); default is <code>false</code>.
*/
public static boolean isRemoteUploadDirectly(PhpProject project) {
return getBoolean(project, PhpProjectProperties.REMOTE_UPLOAD_DIRECTLY, false);
}
/**
* @return remote upload or <code>null</code>.
*/
public static PhpProjectProperties.UploadFiles getRemoteUpload(PhpProject project) {
PhpProjectProperties.UploadFiles uploadFiles = null;
String remoteUpload = project.getEvaluator().getProperty(PhpProjectProperties.REMOTE_UPLOAD);
assert remoteUpload != null;
try {
uploadFiles = PhpProjectProperties.UploadFiles.valueOf(remoteUpload);
} catch (Exception iae) {
// ignored
}
return uploadFiles;
}
/**
* @return debug url (default is DEFAULT_URL).
*/
public static PhpProjectProperties.DebugUrl getDebugUrl(PhpProject project) {
String debugUrl = project.getEvaluator().getProperty(PhpProjectProperties.DEBUG_URL);
if (debugUrl == null) {
return PhpProjectProperties.DebugUrl.DEFAULT_URL;
}
return PhpProjectProperties.DebugUrl.valueOf(debugUrl);
}
/**
* @return list of pairs of remote path (as a String) and local path (absolute path, as a String); empty remote paths are skipped
* as well as invalid local paths
*/
public static List<Pair<String, String>> getDebugPathMapping(PhpProject project) {
List<String> remotes = StringUtils.explode(
getString(project, PhpProjectProperties.DEBUG_PATH_MAPPING_REMOTE, null), PhpProjectProperties.DEBUG_PATH_MAPPING_SEPARATOR);
List<String> locals = StringUtils.explode(
getString(project, PhpProjectProperties.DEBUG_PATH_MAPPING_LOCAL, null), PhpProjectProperties.DEBUG_PATH_MAPPING_SEPARATOR);
int remotesSize = remotes.size();
int localsSize = locals.size();
List<Pair<String, String>> paths = new ArrayList<>(remotesSize);
for (int i = 0; i < remotesSize; ++i) {
String remotePath = remotes.get(i);
if (StringUtils.hasText(remotePath)) {
// if user has only 1 path and local == sources => property is not stored at all!
String l = ""; // NOI18N
if (i < localsSize) {
l = locals.get(i);
}
String localPath = null;
File local = new File(l);
if (local.isAbsolute()) {
if (local.isDirectory()) {
localPath = local.getAbsolutePath();
}
} else {
File subDir = getSourceSubdirectory(project, l);
if (subDir.exists()) {
localPath = subDir.getAbsolutePath();
}
}
if (localPath != null) {
paths.add(Pair.of(remotePath, localPath));
}
}
}
Pair<String, String> copySupportPair = getCopySupportPair(project);
if (copySupportPair != null) {
paths.add(copySupportPair);
}
return paths;
}
/**
* Get debugger proxy (as pair of host, port) or <code>null</code> if it's not set.
* @return debugger proxy (as pair of host, port) or <code>null</code> if it's not set.
*/
public static Pair<String, Integer> getDebugProxy(PhpProject project) {
String host = getString(project, PhpProjectProperties.DEBUG_PROXY_HOST, null);
if (!StringUtils.hasText(host)) {
return null;
}
return Pair.of(host, getInt(project, PhpProjectProperties.DEBUG_PROXY_PORT, PhpProjectProperties.DEFAULT_DEBUG_PROXY_PORT));
}
public static String getHostname(PhpProject project) {
return getString(project, PhpProjectProperties.HOSTNAME, null);
}
public static String getPort(PhpProject project) {
return getString(project, PhpProjectProperties.PORT, null);
}
public static String getInternalRouter(PhpProject project) {
return getString(project, PhpProjectProperties.ROUTER, null);
}
/**
* @return instance of Pair<String, String> or null
*/
private static Pair<String, String> getCopySupportPair(PhpProject project) {
Pair<String, String> copySupportPair = null;
if (ProjectPropertiesSupport.isCopySourcesEnabled(project)) {
File copyTarget = ProjectPropertiesSupport.getCopySourcesTarget(project);
if (copyTarget != null && copyTarget.exists()) {
FileObject copySourceFo = ProjectPropertiesSupport.getSourcesDirectory(project);
if (copySourceFo != null) {
File copySource = FileUtil.toFile(copySourceFo);
if (copySource != null && copySource.exists()) {
copySupportPair = Pair.of(copyTarget.getAbsolutePath(), copySource.getAbsolutePath());
}
}
}
}
return copySupportPair;
}
private static boolean getBoolean(PhpProject project, String property, boolean defaultValue) {
String boolValue = project.getEvaluator().getProperty(property);
if (StringUtils.hasText(boolValue)) {
return Boolean.parseBoolean(boolValue);
}
return defaultValue;
}
private static String getString(PhpProject project, String property, String defaultValue) {
String stringValue = project.getEvaluator().getProperty(property);
if (stringValue == null) {
return defaultValue;
}
return stringValue;
}
private static int getInt(PhpProject project, String property, int defaultValue) {
String stringValue = project.getEvaluator().getProperty(property);
if (stringValue != null) {
try {
return Integer.parseInt(stringValue);
} catch (NumberFormatException exc) {
// ignored
}
}
return defaultValue;
}
@NbBundle.Messages("ProjectPropertiesSupport.project.metadata.saving=Saving project metadata...")
private static void saveTestSources(final PhpProject project, final String propertyName, final File testDir) {
BaseProgressUtils.showProgressDialogAndRun(new Runnable() {
@Override
public void run() {
// XXX reference helper
// relativize text path
File projectDirectory = FileUtil.toFile(project.getProjectDirectory());
String testPath = PropertyUtils.relativizeFile(projectDirectory, testDir);
if (testPath == null) {
// path cannot be relativized => use absolute path (any VCS can be hardly use, of course)
testPath = testDir.getAbsolutePath();
}
PhpProjectProperties.save(project, Collections.singletonMap(propertyName, testPath), Collections.<String, String>emptyMap());
}
}, Bundle.ProjectPropertiesSupport_project_metadata_saving());
}
private static List<FileObject> filterValid(FileObject[] files) {
List<FileObject> validFiles = new ArrayList<>(files.length);
for (FileObject file : files) {
if (file.isValid()) {
validFiles.add(file);
}
}
return validFiles;
}
static FileObject findClosestDir(List<FileObject> directories, FileObject fo) {
assert !directories.isEmpty();
if (fo == null) {
return directories.get(0);
}
File file = FileUtil.toFile(fo);
int idx = 0;
String bestRelPath = null;
for (int i = 0; i < directories.size(); i++) {
File dir = FileUtil.toFile(directories.get(i));
String relPath = PropertyUtils.relativizeFile(dir, file);
if (relPath == null) {
// no relative path possible
continue;
}
if (bestRelPath == null) {
bestRelPath = relPath;
idx = i;
} else {
assert bestRelPath != null;
if (".".equals(relPath)) { // NOI18N
// the folder itself
idx = i;
break;
} else if (!relPath.startsWith("../") // NOI18N
&& bestRelPath.startsWith("../")) { // NOI18N
// subdir
bestRelPath = relPath;
idx = i;
} else {
int relPathLength = relPath.length() - relPath.replace("../", "").length(); // NOI18N
int bestRelPathLength = bestRelPath.length() - bestRelPath.replace("../", "").length(); // NOI18N
if (relPathLength < bestRelPathLength) {
bestRelPath = relPath;
idx = i;
} else if (relPathLength == bestRelPathLength) {
// same number of "../" => compare length
if (relPath.length() < bestRelPath.length()) {
bestRelPath = relPath;
idx = i;
}
}
}
}
}
return directories.get(idx);
}
}