/* * .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; } }