/*
* .NET tools :: Commons
* Copyright (C) 2010 Jose Chillan, Alexandre Victoor and SonarSource
* dev@sonar.codehaus.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
/*
* Created on Apr 16, 2009
*/
package org.sonar.dotnet.tools.commons.visualstudio;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Sets;
/**
* A dot net project extracted from a solution
*
* @author Fabrice BELLINGARD
* @author Jose CHILLAN Apr 16, 2009
*/
public class VisualStudioProject {
private static final Logger LOG = LoggerFactory.getLogger(VisualStudioProject.class);
private String name;
private File projectFile;
private ArtifactType type;
private String assemblyName;
private String realAssemblyName; // assembly name found in the csproj file no matter what
private String rootNamespace;
private File debugOutputDir;
private File releaseOutputDir;
/** Output directory specified from maven */
private String forcedOutputDir;
private Map<String, File> buildConfOutputDirMap;
private File directory;
private boolean test;
private boolean silverlightProject;
private Map<File, SourceFile> sourceFileMap;
private List<BinaryReference> binaryReferences = new ArrayList<BinaryReference>();
/**
* Builds a {@link VisualStudioProject} ...
*
* @param name
* @param projectFile
*/
public VisualStudioProject() {
super();
}
/**
* Gets the relative path of a file contained in the project. <br>
* For example, if the visual studio project is C:/MySolution/MyProject/MyProject.csProj and the file is
* C:/MySolution/MyProject/Dummy/Foo.cs, then the result is Dummy/Foo.cs
*
* @param file
* the file whose relative path is to be computed
* @return the relative path, or <code>null</code> if the file is not in the project subdirectories
*/
public String getRelativePath(File file) {
File canonicalDirectory;
try {
canonicalDirectory = directory.getCanonicalFile();
File canonicalFile = file.getCanonicalFile();
String filePath = canonicalFile.getPath();
String directoryPath = canonicalDirectory.getPath();
if ( !filePath.startsWith(directoryPath)) {
// The file is not in the directory
return null;
}
return StringUtils.removeStart(StringUtils.removeStart(filePath, directoryPath), "\\");
} catch (IOException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("io exception with file " + file, e);
}
return null;
}
}
/**
* Returns the name.
*
* @return The name to return.
*/
public String getName() {
return this.name;
}
/**
* Returns the projectFile.
*
* @return The projectFile to return.
*/
public File getProjectFile() {
return this.projectFile;
}
/**
* Returns the type.
*
* @return The type to return.
*/
public ArtifactType getType() {
return this.type;
}
/**
* Provides the location of the generated artifact(s) of this project according to the build configuration(s) used.
*
* @param buildConfigurations
* Visual Studio build configurations used to generate the project
* @return
*/
public File getArtifactDirectory(String buildConfigurations) {
File artifactDirectory = null;
if (StringUtils.isNotEmpty(forcedOutputDir)) {
// first trying to use forcedOutputDir as a relative path
File assemblyDirectory = new File(directory, forcedOutputDir);
if ( !assemblyDirectory.exists()) {
// path specified "forcedOutputDir" should be absolute,
// not relative to the project root directory
assemblyDirectory = new File(forcedOutputDir);
}
artifactDirectory = assemblyDirectory;
} else if (StringUtils.isEmpty(buildConfigurations) || StringUtils.contains(buildConfigurations, "Debug")) {
artifactDirectory = debugOutputDir;
} else if (buildConfOutputDirMap == null) {
// no custom build conf, debug not wanted, get the release one
artifactDirectory = releaseOutputDir;
} else {
// get the first one found
String buildConfiguration = Arrays.asList(StringUtils.split(buildConfigurations, ",; ")).get(0);
artifactDirectory = buildConfOutputDirMap.get(buildConfiguration);
}
return artifactDirectory;
}
/**
* Gets the generated assembly according to the build configurations
*
* @param buildConfigurations
* Visual Studio build configurations used to generate the project
* @return
*/
public File getArtifact(String buildConfigurations) {
File artifactDirectory = getArtifactDirectory(buildConfigurations);
return new File(artifactDirectory, getArtifactName());
}
/**
* Gets the generated assemblies according to the build configurations. There is zero or one single assembly generated except for web
* assemblies.
*
* @param buildConfigurations
* Visual Studio build configurations used to generate the project
* @return a Set of the generated assembly files. If no files found, the set will be empty.
*/
public Set<File> getGeneratedAssemblies(String buildConfigurations) {
Set<File> result = Sets.newHashSet();
File assembly = getArtifact(buildConfigurations);
if (assembly.exists()) {
result.add(assembly);
} else {
LOG.info("Skipping the non generated assembly of project : {}", name);
}
return result;
}
/**
* Gets the name of the artifact.
*
* @return
*/
public String getArtifactName() {
return realAssemblyName + "." + getExtension();
}
@Override
public String toString() {
return "Project(name=" + name + ", type=" + type + ", test=" + test + ", directory=" + directory + ", file=" + projectFile
+ ", assemblyName=" + assemblyName + ", rootNamespace=" + rootNamespace + ", debugDir=" + debugOutputDir + ", releaseDir="
+ releaseOutputDir + ")";
}
/**
* Returns the assemblyName.
*
* @return The assemblyName to return.
*/
public String getAssemblyName() {
return this.assemblyName;
}
/**
* Sets the assemblyName.
*
* @param assemblyName
* The assemblyName to set.
*/
void setAssemblyName(String assemblyName) {
this.assemblyName = assemblyName;
if (StringUtils.isEmpty(realAssemblyName)) {
realAssemblyName = assemblyName;
}
}
/**
* Returns the rootNamespace.
*
* @return The rootNamespace to return.
*/
public String getRootNamespace() {
return this.rootNamespace;
}
/**
* Sets the rootNamespace.
*
* @param rootNamespace
* The rootNamespace to set.
*/
void setRootNamespace(String rootNamespace) {
this.rootNamespace = rootNamespace;
}
/**
* Returns the debugOutputDir.
*
* @return The debugOutputDir to return.
*/
public File getDebugOutputDir() {
return this.debugOutputDir;
}
/**
* Sets the debugOutputDir.
*
* @param debugOutputDir
* The debugOutputDir to set.
*/
void setDebugOutputDir(File debugOutputDir) {
this.debugOutputDir = debugOutputDir;
}
/**
* Sets the releaseOutputDir.
*
* @param releaseOutputDir
* The releaseOutputDir to set.
*/
void setReleaseOutputDir(File releaseOutputDir) {
this.releaseOutputDir = releaseOutputDir;
}
/**
* Returns the directory.
*
* @return The directory to return.
*/
public File getDirectory() {
return this.directory;
}
/**
* Sets the root directory of the project. For a regular project, this is where is located the csproj file.
*
* @param directory
* The directory to set.
*/
void setDirectory(File directory) {
try {
this.directory = directory.getCanonicalFile();
} catch (IOException e) {
LOG.warn("Invalid project directory : " + directory);
}
}
/**
* Sets the name.
*
* @param name
* The name to set.
*/
void setName(String name) {
this.name = name;
}
/**
* Sets the projectFile.
*
* @param projectFile
* The projectFile to set.
*/
void setProjectFile(File projectFile) {
this.projectFile = projectFile;
}
/**
* Sets the type.
*
* @param type
* The type to set.
*/
void setType(ArtifactType type) {
this.type = type;
}
/**
* Returns the test.
*
* @return The test to return.
*/
public boolean isTest() {
return this.test;
}
/**
* Sets the test.
*
* @param test
* The test to set.
*/
void setTest(boolean test) {
this.test = test;
for (BinaryReference reference : binaryReferences) {
String scope = test ? "test" : "compile";
reference.setScope(scope);
}
}
/**
* Gets the artifact extension (.dll or .exe)
*
* @return the extension
*/
public String getExtension() {
String result = null;
switch (type) {
case EXECUTABLE:
result = "exe";
break;
case LIBRARY:
case WEB:
result = "dll";
break;
default:
result = null;
}
return result;
}
/**
* Gets all the files contained in the project
*
* @return
*/
public Collection<SourceFile> getSourceFiles() {
if (sourceFileMap == null) {
initializeSourceFileMap();
}
return sourceFileMap.values();
}
private void initializeSourceFileMap() {
Map<File, SourceFile> allFiles = new LinkedHashMap<File, SourceFile>(); // Case of a regular project
if (projectFile != null) {
List<String> filesPath = ModelFactory.getFilesPath(projectFile);
for (String filePath : filesPath) {
try {
// We build the file and retrieves its canonical path
File file = new File(directory, filePath).getCanonicalFile();
String fileName = file.getName();
String folder = StringUtils.replace(StringUtils.removeEnd(StringUtils.removeEnd(filePath, fileName), "\\"), "\\", "/");
SourceFile sourceFile = new SourceFile(this, file, folder, fileName);
allFiles.put(file, sourceFile);
} catch (IOException e) {
LOG.error("Bad file :" + filePath, e);
}
}
} else {
// For web projects, we take all the C# files
List<File> csharpFiles = listRecursiveFiles(directory, ".cs");
for (File file : csharpFiles) {
SourceFile sourceFile = new SourceFile(this, file, file.getParent(), file.getName());
allFiles.put(file, sourceFile);
}
}
this.sourceFileMap = allFiles;
}
Map<File, SourceFile> getSourceFileMap() {
if (sourceFileMap == null) {
initializeSourceFileMap();
}
return sourceFileMap;
}
/**
* Recursively lists the files of a directory
*
* @param dir
* the directory to list
* @param extension
* @return
*/
private List<File> listRecursiveFiles(File dir, String extension) {
List<File> result = new ArrayList<File>();
File[] content = dir.listFiles();
if (content != null) {
for (File file : content) {
if (file.isDirectory()) {
List<File> matching = listRecursiveFiles(file, extension);
result.addAll(matching);
} else {
// Look for matching file names
if (StringUtils.endsWithIgnoreCase(file.getName(), extension)) {
result.add(file);
}
}
}
}
return result;
}
/**
* Gets the source representation of a given file.
*
* @param file
* the file to retrieve
* @return the associated source file, or <code>null</code> if the file is not included in the assembly.
*/
public SourceFile getFile(File file) {
File currentFile;
try {
currentFile = file.getCanonicalFile();
} catch (IOException e) {
// File not found
if (LOG.isDebugEnabled()) {
LOG.debug("file not found " + file, e);
}
return null;
}
// We ensure the source files are loaded
getSourceFiles();
return sourceFileMap.get(currentFile);
}
/**
* Test if this project is a parent directory of the given file.
*
* @param file
* the file to check
* @return <code>true</code> if the file is under this project
*/
public boolean isParentDirectoryOf(File file) {
return ModelFactory.isSubDirectory(directory, file);
}
/**
* Checks if the project contains a given source file.
*
* @param file
* the file to check
* @return <code>true</code> if the project contains the file
*/
public boolean contains(File file) {
try {
File currentFile = file.getCanonicalFile();
// We ensure the source files are loaded
getSourceFiles();
return sourceFileMap.containsKey(currentFile);
} catch (IOException e) {
LOG.debug("file error", e);
}
return false;
}
public boolean isWebProject() {
return (projectFile == null);
}
/**
* @param silverlightProject
* true if it is a silverlight project
*/
void setSilverlightProject(boolean silverlightProject) {
this.silverlightProject = silverlightProject;
}
/**
* @return true if it is a silverlight project
*/
public boolean isSilverlightProject() {
return silverlightProject;
}
public List<BinaryReference> getBinaryReferences() {
return binaryReferences;
}
void setBinaryReferences(List<BinaryReference> binaryReferences) {
this.binaryReferences = binaryReferences;
}
void setBuildConfOutputDirMap(Map<String, File> buildConfOutputDirMap) {
this.buildConfOutputDirMap = buildConfOutputDirMap;
}
void setForcedOutputDir(String forcedOutputDir) {
this.forcedOutputDir = forcedOutputDir;
}
}