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; 029 030import javax.annotation.Nullable; 031 032import org.sonar.api.batch.bootstrap.ProjectDefinition; 033import org.sonar.api.batch.fs.InputFile; 034import org.sonar.api.utils.PathUtils; 035 036/** 037 * Intended to be used in unit tests that need to create {@link InputFile}s. 038 * An InputFile is unambiguously identified by a <b>module key</b> and a <b>relative path</b>, so these parameters are mandatory. 039 * 040 * A module base directory is only needed to construct absolute paths. 041 * 042 * Examples of usage of the constructors: 043 * 044 * <pre> 045 * InputFile file1 = TestInputFileBuilder.create("module1", "myfile.java").build(); 046 * InputFile file2 = TestInputFileBuilder.create("", fs.baseDir(), myfile).build(); 047 * </pre> 048 * 049 * file1 will have the "module1" as both module key and module base directory. 050 * file2 has an empty string as module key, and a relative path which is the path from the filesystem base directory to myfile. 051 * 052 * @since 6.3 053 */ 054public class TestInputFileBuilder { 055 private static int batchId = 1; 056 057 private final int id; 058 private final String relativePath; 059 private final String moduleKey; 060 private Path moduleBaseDir; 061 private String language; 062 private InputFile.Type type = InputFile.Type.MAIN; 063 private InputFile.Status status; 064 private int lines = -1; 065 private Charset charset; 066 private int lastValidOffset = -1; 067 private String hash; 068 private int nonBlankLines; 069 private int[] originalLineOffsets; 070 private boolean publish = true; 071 private String contents; 072 073 /** 074 * Create a InputFile identified by the given module key and relative path. 075 * The module key will also be used as the module's base directory. 076 */ 077 public TestInputFileBuilder(String moduleKey, String relativePath) { 078 this(moduleKey, relativePath, batchId++); 079 } 080 081 /** 082 * Create a InputFile with a given module key and module base directory. 083 * The relative path is generated comparing the file path to the module base directory. 084 * filePath must point to a file that is within the module base directory. 085 */ 086 public TestInputFileBuilder(String moduleKey, File moduleBaseDir, File filePath) { 087 String relativePath = moduleBaseDir.toPath().relativize(filePath.toPath()).toString(); 088 this.moduleKey = moduleKey; 089 setModuleBaseDir(moduleBaseDir.toPath()); 090 this.relativePath = PathUtils.sanitize(relativePath); 091 this.id = batchId++; 092 } 093 094 public TestInputFileBuilder(String moduleKey, String relativePath, int id) { 095 this.moduleKey = moduleKey; 096 setModuleBaseDir(Paths.get(moduleKey)); 097 this.relativePath = PathUtils.sanitize(relativePath); 098 this.id = id; 099 } 100 101 public static TestInputFileBuilder create(String moduleKey, File moduleBaseDir, File filePath) { 102 return new TestInputFileBuilder(moduleKey, moduleBaseDir, filePath); 103 } 104 105 public static TestInputFileBuilder create(String moduleKey, String relativePath) { 106 return new TestInputFileBuilder(moduleKey, relativePath); 107 } 108 109 public static int nextBatchId() { 110 return batchId++; 111 } 112 113 public TestInputFileBuilder setModuleBaseDir(Path moduleBaseDir) { 114 try { 115 this.moduleBaseDir = moduleBaseDir.normalize().toRealPath(LinkOption.NOFOLLOW_LINKS); 116 } catch (IOException e) { 117 this.moduleBaseDir = moduleBaseDir.normalize(); 118 } 119 return this; 120 } 121 122 public TestInputFileBuilder setLanguage(@Nullable String language) { 123 this.language = language; 124 return this; 125 } 126 127 public TestInputFileBuilder setType(InputFile.Type type) { 128 this.type = type; 129 return this; 130 } 131 132 public TestInputFileBuilder setStatus(InputFile.Status status) { 133 this.status = status; 134 return this; 135 } 136 137 public TestInputFileBuilder setLines(int lines) { 138 this.lines = lines; 139 return this; 140 } 141 142 public TestInputFileBuilder setCharset(Charset charset) { 143 this.charset = charset; 144 return this; 145 } 146 147 public TestInputFileBuilder setLastValidOffset(int lastValidOffset) { 148 this.lastValidOffset = lastValidOffset; 149 return this; 150 } 151 152 public TestInputFileBuilder setHash(String hash) { 153 this.hash = hash; 154 return this; 155 } 156 157 /** 158 * Set contents of the file and calculates metadata from it. 159 * The contents will be returned by {@link InputFile#contents()} and {@link InputFile#inputStream()} and can be 160 * inconsistent with the actual physical file pointed by {@link InputFile#path()}, {@link InputFile#absolutePath()}, etc. 161 */ 162 public TestInputFileBuilder setContents(String content) { 163 this.contents = content; 164 initMetadata(content); 165 return this; 166 } 167 168 public TestInputFileBuilder setNonBlankLines(int nonBlankLines) { 169 this.nonBlankLines = nonBlankLines; 170 return this; 171 } 172 173 public TestInputFileBuilder setOriginalLineOffsets(int[] originalLineOffsets) { 174 this.originalLineOffsets = originalLineOffsets; 175 return this; 176 } 177 178 public TestInputFileBuilder setPublish(boolean publish) { 179 this.publish = publish; 180 return this; 181 } 182 183 public TestInputFileBuilder setMetadata(Metadata metadata) { 184 this.setLines(metadata.lines()); 185 this.setLastValidOffset(metadata.lastValidOffset()); 186 this.setNonBlankLines(metadata.nonBlankLines()); 187 this.setHash(metadata.hash()); 188 this.setOriginalLineOffsets(metadata.originalLineOffsets()); 189 return this; 190 } 191 192 public TestInputFileBuilder initMetadata(String content) { 193 return setMetadata(new FileMetadata().readMetadata(new StringReader(content))); 194 } 195 196 public DefaultInputFile build() { 197 DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, id); 198 indexedFile.setLanguage(language); 199 DefaultInputFile inputFile = new DefaultInputFile(indexedFile, 200 f -> f.setMetadata(new Metadata(lines, nonBlankLines, hash, originalLineOffsets, lastValidOffset)), 201 contents); 202 inputFile.setStatus(status); 203 inputFile.setCharset(charset); 204 inputFile.setPublish(publish); 205 return inputFile; 206 } 207 208 public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) { 209 ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey); 210 definition.setBaseDir(baseDir); 211 return new DefaultInputModule(definition, TestInputFileBuilder.nextBatchId()); 212 } 213}