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    }