001 /* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2014 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * SonarQube 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 * SonarQube 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 */ 020 package org.sonar.api.resources; 021 022 import org.apache.commons.lang.StringUtils; 023 import org.apache.commons.lang.builder.ToStringBuilder; 024 import org.sonar.api.batch.SensorContext; 025 import org.sonar.api.scan.filesystem.PathResolver; 026 import org.sonar.api.utils.WildcardPattern; 027 028 import javax.annotation.CheckForNull; 029 030 import java.util.List; 031 032 /** 033 * This class is an implementation of a resource of type FILE 034 * 035 * @since 1.10 036 */ 037 public class File extends Resource { 038 039 public static final String SCOPE = Scopes.FILE; 040 041 private String directoryDeprecatedKey; 042 private String filename; 043 private Language language; 044 private Directory parent; 045 private String qualifier = Qualifiers.FILE; 046 047 private File() { 048 // Used by factory method 049 } 050 051 /** 052 * File in project. Key is the path relative to project source directories. It is not the absolute path and it does not include the path 053 * to source directories. Example : <code>new File("org/sonar/foo.sql")</code>. The absolute path may be 054 * c:/myproject/src/main/sql/org/sonar/foo.sql. Project root is c:/myproject and source dir is src/main/sql. 055 * @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} 056 */ 057 @Deprecated 058 public File(String relativePathFromSourceDir) { 059 if (relativePathFromSourceDir == null) { 060 throw new IllegalArgumentException("File key is null"); 061 } 062 String realKey = parseKey(relativePathFromSourceDir); 063 if (realKey.indexOf(Directory.SEPARATOR) >= 0) { 064 this.directoryDeprecatedKey = Directory.parseKey(StringUtils.substringBeforeLast(relativePathFromSourceDir, Directory.SEPARATOR)); 065 this.filename = StringUtils.substringAfterLast(realKey, Directory.SEPARATOR); 066 realKey = new StringBuilder().append(this.directoryDeprecatedKey).append(Directory.SEPARATOR).append(filename).toString(); 067 068 } else { 069 this.filename = relativePathFromSourceDir; 070 } 071 setDeprecatedKey(realKey); 072 } 073 074 /** 075 * Creates a file from its containing directory and name 076 * @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} 077 */ 078 @Deprecated 079 public File(String relativeDirectoryPathFromSourceDir, String filename) { 080 this.filename = StringUtils.trim(filename); 081 if (StringUtils.isBlank(relativeDirectoryPathFromSourceDir)) { 082 setDeprecatedKey(filename); 083 084 } else { 085 this.directoryDeprecatedKey = Directory.parseKey(relativeDirectoryPathFromSourceDir); 086 setDeprecatedKey(new StringBuilder().append(directoryDeprecatedKey).append(Directory.SEPARATOR).append(this.filename).toString()); 087 } 088 } 089 090 /** 091 * Creates a File from its language and its key 092 * @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} 093 */ 094 @Deprecated 095 public File(Language language, String relativePathFromSourceDir) { 096 this(relativePathFromSourceDir); 097 this.language = language; 098 } 099 100 /** 101 * Creates a File from language, directory and filename 102 * @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} 103 */ 104 @Deprecated 105 public File(Language language, String relativeDirectoryPathFromSourceDir, String filename) { 106 this(relativeDirectoryPathFromSourceDir, filename); 107 this.language = language; 108 } 109 110 /** 111 * {@inheritDoc} 112 * 113 * @see Resource#getParent() 114 */ 115 @Override 116 public Directory getParent() { 117 if (parent == null) { 118 parent = new Directory(directoryDeprecatedKey); 119 } 120 return parent; 121 } 122 123 private static String parseKey(String key) { 124 if (StringUtils.isBlank(key)) { 125 return null; 126 } 127 String normalizedKey = key; 128 normalizedKey = normalizedKey.replace('\\', '/'); 129 normalizedKey = StringUtils.trim(normalizedKey); 130 return normalizedKey; 131 } 132 133 /** 134 * {@inheritDoc} 135 * 136 * @see Resource#matchFilePattern(String) 137 */ 138 @Override 139 public boolean matchFilePattern(String antPattern) { 140 WildcardPattern matcher = WildcardPattern.create(antPattern, Directory.SEPARATOR); 141 return matcher.match(getKey()); 142 } 143 144 /** 145 * Creates a File from an io.file and a list of sources directories 146 * @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} 147 */ 148 @Deprecated 149 @CheckForNull 150 public static File fromIOFile(java.io.File file, List<java.io.File> sourceDirs) { 151 PathResolver.RelativePath relativePath = new PathResolver().relativePath(sourceDirs, file); 152 if (relativePath != null) { 153 return new File(relativePath.path()); 154 } 155 return null; 156 } 157 158 /** 159 * Creates a {@link File} from an absolute {@link java.io.File} and a module. 160 * The returned {@link File} can be then passed for example to 161 * {@link SensorContext#saveMeasure(Resource, org.sonar.api.measures.Measure)}. 162 * @param file absolute path to a file 163 * @param module 164 * @return null if the file is not under module basedir. 165 */ 166 @CheckForNull 167 public static File fromIOFile(java.io.File file, Project module) { 168 String relativePathFromBasedir = new PathResolver().relativePath(module.getFileSystem().getBasedir(), file); 169 if (relativePathFromBasedir != null) { 170 return File.create(relativePathFromBasedir); 171 } 172 return null; 173 } 174 175 /** 176 * {@inheritDoc} 177 * 178 * @see Resource#getName() 179 */ 180 @Override 181 public String getName() { 182 return filename; 183 } 184 185 /** 186 * {@inheritDoc} 187 * 188 * @see Resource#getLongName() 189 */ 190 @Override 191 public String getLongName() { 192 return StringUtils.defaultIfBlank(getPath(), getKey()); 193 } 194 195 /** 196 * {@inheritDoc} 197 * 198 * @see Resource#getDescription() 199 */ 200 @Override 201 public String getDescription() { 202 return null; 203 } 204 205 /** 206 * {@inheritDoc} 207 * 208 * @see Resource#getLanguage() 209 */ 210 @Override 211 public Language getLanguage() { 212 return language; 213 } 214 215 /** 216 * Sets the language of the file 217 */ 218 public void setLanguage(Language language) { 219 this.language = language; 220 } 221 222 /** 223 * @return SCOPE_ENTITY 224 */ 225 @Override 226 public final String getScope() { 227 return SCOPE; 228 } 229 230 /** 231 * Returns the qualifier associated to this File. Should be QUALIFIER_FILE or QUALIFIER_UNIT_TEST_CLASS 232 */ 233 @Override 234 public String getQualifier() { 235 return qualifier; 236 } 237 238 public void setQualifier(String qualifier) { 239 this.qualifier = qualifier; 240 } 241 242 /** 243 * Create a File that is partially initialized. But that's enough to call for example 244 * {@link SensorContext#saveMeasure(Resource, org.sonar.api.measures.Measure)} when resources are already indexed. 245 * Internal use only. 246 * @since 4.2 247 */ 248 public static File create(String relativePathFromBasedir) { 249 File file = new File(); 250 String normalizedPath = normalize(relativePathFromBasedir); 251 file.setKey(normalizedPath); 252 file.setPath(normalizedPath); 253 String directoryPath; 254 if (normalizedPath != null && normalizedPath.contains(Directory.SEPARATOR)) { 255 directoryPath = StringUtils.substringBeforeLast(normalizedPath, Directory.SEPARATOR); 256 } else { 257 directoryPath = Directory.SEPARATOR; 258 } 259 file.parent = Directory.create(directoryPath); 260 return file; 261 } 262 263 /** 264 * Create a file that is fully initialized. Use for indexing resources. 265 * Internal use only. 266 * @since 4.2 267 */ 268 public static File create(String relativePathFromBasedir, String relativePathFromSourceDir, Language language, boolean unitTest) { 269 File file = create(relativePathFromBasedir); 270 file.setLanguage(language); 271 if (relativePathFromSourceDir.contains(Directory.SEPARATOR)) { 272 file.filename = StringUtils.substringAfterLast(relativePathFromSourceDir, Directory.SEPARATOR); 273 file.directoryDeprecatedKey = Directory.parseKey(StringUtils.substringBeforeLast(relativePathFromSourceDir, Directory.SEPARATOR)); 274 file.setDeprecatedKey(file.directoryDeprecatedKey + Directory.SEPARATOR + file.filename); 275 } else { 276 file.filename = relativePathFromSourceDir; 277 file.directoryDeprecatedKey = Directory.ROOT; 278 file.setDeprecatedKey(file.filename); 279 } 280 if (unitTest) { 281 file.setQualifier(Qualifiers.UNIT_TEST_FILE); 282 } 283 file.parent.setDeprecatedKey(file.directoryDeprecatedKey); 284 return file; 285 } 286 287 @Override 288 public String toString() { 289 return new ToStringBuilder(this) 290 .append("key", getKey()) 291 .append("deprecatedKey", getDeprecatedKey()) 292 .append("path", getPath()) 293 .append("dir", directoryDeprecatedKey) 294 .append("filename", filename) 295 .append("language", language) 296 .toString(); 297 } 298 }