001/* 002 * SonarQube 003 * Copyright (C) 2009-2017 SonarSource SA 004 * mailto:info AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.api.batch.fs.internal; 021 022import java.io.File; 023import java.io.IOException; 024import java.io.StringReader; 025import java.nio.charset.Charset; 026import java.nio.file.LinkOption; 027import java.nio.file.Path; 028import java.nio.file.Paths; 029import javax.annotation.Nullable; 030import org.sonar.api.batch.bootstrap.ProjectDefinition; 031import org.sonar.api.batch.fs.InputFile; 032import org.sonar.api.utils.PathUtils; 033 034/** 035 * Intended to be used in unit tests that need to create {@link InputFile}s. 036 * An InputFile is unambiguously identified by a <b>module key</b> and a <b>relative path</b>, so these parameters are mandatory. 037 * 038 * A module base directory is only needed to construct absolute paths. 039 * 040 * Examples of usage of the constructors: 041 * 042 * <pre> 043 * InputFile file1 = TestInputFileBuilder.create("module1", "myfile.java").build(); 044 * InputFile file2 = TestInputFileBuilder.create("", fs.baseDir(), myfile).build(); 045 * </pre> 046 * 047 * file1 will have the "module1" as both module key and module base directory. 048 * file2 has an empty string as module key, and a relative path which is the path from the filesystem base directory to myfile. 049 * 050 * @since 6.3 051 */ 052public class TestInputFileBuilder { 053 private static int batchId = 1; 054 055 private final int id; 056 private final String relativePath; 057 private final String moduleKey; 058 private Path moduleBaseDir; 059 private String language; 060 private InputFile.Type type = InputFile.Type.MAIN; 061 private InputFile.Status status; 062 private int lines = -1; 063 private Charset charset; 064 private int lastValidOffset = -1; 065 private String hash; 066 private int nonBlankLines; 067 private int[] originalLineOffsets = new int[0]; 068 private boolean publish = true; 069 private String contents; 070 071 /** 072 * Create a InputFile identified by the given module key and relative path. 073 * The module key will also be used as the module's base directory. 074 */ 075 public TestInputFileBuilder(String moduleKey, String relativePath) { 076 this(moduleKey, relativePath, batchId++); 077 } 078 079 /** 080 * Create a InputFile with a given module key and module base directory. 081 * The relative path is generated comparing the file path to the module base directory. 082 * filePath must point to a file that is within the module base directory. 083 */ 084 public TestInputFileBuilder(String moduleKey, File moduleBaseDir, File filePath) { 085 String relativePath = moduleBaseDir.toPath().relativize(filePath.toPath()).toString(); 086 this.moduleKey = moduleKey; 087 setModuleBaseDir(moduleBaseDir.toPath()); 088 this.relativePath = PathUtils.sanitize(relativePath); 089 this.id = batchId++; 090 } 091 092 public TestInputFileBuilder(String moduleKey, String relativePath, int id) { 093 this.moduleKey = moduleKey; 094 setModuleBaseDir(Paths.get(moduleKey)); 095 this.relativePath = PathUtils.sanitize(relativePath); 096 this.id = id; 097 } 098 099 public static TestInputFileBuilder create(String moduleKey, File moduleBaseDir, File filePath) { 100 return new TestInputFileBuilder(moduleKey, moduleBaseDir, filePath); 101 } 102 103 public static TestInputFileBuilder create(String moduleKey, String relativePath) { 104 return new TestInputFileBuilder(moduleKey, relativePath); 105 } 106 107 public static int nextBatchId() { 108 return batchId++; 109 } 110 111 public TestInputFileBuilder setModuleBaseDir(Path moduleBaseDir) { 112 try { 113 this.moduleBaseDir = moduleBaseDir.normalize().toRealPath(LinkOption.NOFOLLOW_LINKS); 114 } catch (IOException e) { 115 this.moduleBaseDir = moduleBaseDir.normalize(); 116 } 117 return this; 118 } 119 120 public TestInputFileBuilder setLanguage(@Nullable String language) { 121 this.language = language; 122 return this; 123 } 124 125 public TestInputFileBuilder setType(InputFile.Type type) { 126 this.type = type; 127 return this; 128 } 129 130 public TestInputFileBuilder setStatus(InputFile.Status status) { 131 this.status = status; 132 return this; 133 } 134 135 public TestInputFileBuilder setLines(int lines) { 136 this.lines = lines; 137 return this; 138 } 139 140 public TestInputFileBuilder setCharset(Charset charset) { 141 this.charset = charset; 142 return this; 143 } 144 145 public TestInputFileBuilder setLastValidOffset(int lastValidOffset) { 146 this.lastValidOffset = lastValidOffset; 147 return this; 148 } 149 150 public TestInputFileBuilder setHash(String hash) { 151 this.hash = hash; 152 return this; 153 } 154 155 /** 156 * Set contents of the file and calculates metadata from it. 157 * The contents will be returned by {@link InputFile#contents()} and {@link InputFile#inputStream()} and can be 158 * inconsistent with the actual physical file pointed by {@link InputFile#path()}, {@link InputFile#absolutePath()}, etc. 159 */ 160 public TestInputFileBuilder setContents(String content) { 161 this.contents = content; 162 initMetadata(content); 163 return this; 164 } 165 166 public TestInputFileBuilder setNonBlankLines(int nonBlankLines) { 167 this.nonBlankLines = nonBlankLines; 168 return this; 169 } 170 171 public TestInputFileBuilder setOriginalLineOffsets(int[] originalLineOffsets) { 172 this.originalLineOffsets = originalLineOffsets; 173 return this; 174 } 175 176 public TestInputFileBuilder setPublish(boolean publish) { 177 this.publish = publish; 178 return this; 179 } 180 181 public TestInputFileBuilder setMetadata(Metadata metadata) { 182 this.setLines(metadata.lines()); 183 this.setLastValidOffset(metadata.lastValidOffset()); 184 this.setNonBlankLines(metadata.nonBlankLines()); 185 this.setHash(metadata.hash()); 186 this.setOriginalLineOffsets(metadata.originalLineOffsets()); 187 return this; 188 } 189 190 public TestInputFileBuilder initMetadata(String content) { 191 return setMetadata(new FileMetadata().readMetadata(new StringReader(content))); 192 } 193 194 public DefaultInputFile build() { 195 DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleBaseDir.resolve(relativePath), moduleKey, relativePath, relativePath, type, language, id, new SensorStrategy()); 196 DefaultInputFile inputFile = new DefaultInputFile(indexedFile, 197 f -> f.setMetadata(new Metadata(lines, nonBlankLines, hash, originalLineOffsets, lastValidOffset)), 198 contents); 199 inputFile.setStatus(status); 200 inputFile.setCharset(charset); 201 inputFile.setPublished(publish); 202 return inputFile; 203 } 204 205 public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) { 206 ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey).setBaseDir(baseDir).setWorkDir(new File(baseDir, ".sonar")); 207 return newDefaultInputModule(definition); 208 } 209 210 public static DefaultInputModule newDefaultInputModule(ProjectDefinition projectDefinition) { 211 return new DefaultInputModule(projectDefinition, TestInputFileBuilder.nextBatchId()); 212 } 213}