View Javadoc

1   package de.matthias_burbach.deputy.core.repository;
2   
3   import java.io.File;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.HashMap;
7   import java.util.List;
8   import java.util.Map;
9   
10  import org.jdom.Document;
11  import org.jdom.Element;
12  import org.jdom.xpath.XPath;
13  
14  import de.matthias_burbach.deputy.core.project.VersionComparator;
15  
16  /***
17   * Scans a given path and artifact id for all POMs of this artifact and returns
18   * all versions of that artifact available there.
19   *
20   * @author Matthias Burbach
21   */
22  public class VersionScanner {
23      /***
24       * A temporary table to speed up the list method on a directory.
25       * Maps dir names of type {@link java.lang.String} to file name arrays of
26       * type String[].
27       */
28      private static Map fileListMap = new HashMap();
29  
30      /***
31       * The cache for lists of versions that have been returned already.
32       */
33      private Map resultCache = new HashMap();
34  
35      /***
36       * The comparator used to sort result lists.
37       */
38      private VersionComparator versionComparator = new VersionComparator();
39  
40      /***
41       * Clears the internal cache of already scanned directories.
42       * The cache is shared by all instances of this class.
43       */
44      public static void clearCache() {
45          fileListMap = new HashMap();
46      }
47  
48      /***
49       * Scans a given path and artifact id for all POMs of this artifact and
50       * returns all versions of that artifact available there.<br/>
51       * Returns the result from a local cache if possible.
52       *
53       * @param path The absolute file system path of a directory to scan.
54       * @param artifactId The id of the artifact to scan versions for.
55       * @return The list of versions of type {@link String}.
56       *         Is never <code>null</code>.
57       */
58      public List getAllVersions(final String path, final String artifactId) {
59          final String key = path + "#" + artifactId;
60          List result = (List) resultCache.get(key);
61          if (result == null) {
62              result = doGetAllVersions(path, artifactId);
63              if (result != null) {
64                  Collections.sort(result, versionComparator);
65                  resultCache.put(key, result);
66              }
67          }
68          return result;
69      }
70  
71      /***
72       * Scans a given path and artifact id for all POMs of this artifact and
73       * returns all versions of that artifact available there.
74       *
75       * @param path The absolute file system path of a directory to scan.
76       * @param artifactId The id of the artifact to scan versions for.
77       * @return The list of versions of type {@link String}.
78       *         Is never <code>null</code>.
79       */
80      private List doGetAllVersions(final String path, final String artifactId) {
81          List result = new ArrayList();
82          String[] fileNames = list(new File(path));
83          for (int i = 0; i < fileNames.length; i++) {
84              String fileName = fileNames[i];
85              if (!isExcludedFileName(artifactId, fileName)) {
86                  String version = extractVersion(path, fileName);
87                  if (version != null
88                          && fileName.equals(
89                                  artifactId + "-" + version + ".pom")) {
90                      result.add(version);
91                  }
92              }
93          }
94          return result;
95      }
96  
97      /***
98       * Loads and parses the POM XML document and returns the currentVersion.
99       *
100      * @param path The absolute file system path of the file to parse
101      *             without the file name.
102      * @param fileName The simple name of the file to parse.
103      * @return The current version parsed or null in case of any failure.
104      */
105     private String extractVersion(final String path, final String fileName) {
106         String result = null;
107         if (fileName.startsWith("vrp-") && fileName.indexOf("SNAPSHOT") == -1) {
108             result = extractVersionFromFileName(fileName);
109         } else {
110             Document document = PomLoader.getInstance().loadPom(
111                     path + File.separator + fileName);
112             try {
113                 result = parseElementText(document, "project/currentVersion");
114             } catch (Exception e) {
115                 e.printStackTrace();
116             }
117         }
118         return result;
119     }
120 
121     /***
122      * Optimizes overall performance by assuming that the file name is composed
123      * as artifactId-version.pom and that the version does not contain a '-'.
124      *
125      * @param fileName The file name to extract the version from.
126      * @return The version extracted.
127      */
128     private String extractVersionFromFileName(final String fileName) {
129         int versionStartIndex = fileName.lastIndexOf("-") + 1;
130         int versionEndIndex = fileName.length() - ".pom".length();
131         String result = fileName.substring(versionStartIndex, versionEndIndex);
132         return result;
133     }
134 
135     /***
136      * Tries to parse the element named <code>elementName.</code>.
137      *
138      * @param xmlNode The XML node to that contains the element to parse.
139      * @param elementName The name of the element to parse.
140      * @return The contents of the element to parse or <code>null</code>.
141      * @throws Exception if anything goes unexpectedly wrong
142      */
143     private String parseElementText(
144             final Object xmlNode,
145             final String elementName)
146             throws Exception {
147         Element element =
148             (Element) XPath.newInstance(elementName).selectSingleNode(xmlNode);
149         String result = null;
150         if (element != null) {
151             result = element.getText();
152         }
153         return result;
154     }
155 
156     /***
157      * @param artifactId The id of the artifact to scan versions for.
158      * @param fileName The simple name of the file to check.
159      * @return <code>true</code> if the fileName is to be excluded from the
160      *         version scanning
161      */
162     private boolean isExcludedFileName(
163             final String artifactId,
164             final String fileName) {
165         boolean result = true;
166         String lowerCaseFileName = fileName.toLowerCase();
167         if (fileName.startsWith(artifactId + "-")
168                 && lowerCaseFileName.endsWith(".pom")
169                 && !lowerCaseFileName.endsWith("openedition.pom")
170                 && lowerCaseFileName.indexOf("snapshot-") == -1
171                 && !isTimestampedFileName(fileName)) {
172             result = false;
173         }
174         return result;
175     }
176 
177     /***
178      * @param fileName The file name to check.
179      * @return <code>true</code> if the file name has the format
180      *         name-yyyymmdd.hhmmss.pom
181      */
182     private boolean isTimestampedFileName(final String fileName) {
183         boolean result = false;
184         if (fileName.matches(
185                 ".*-//d//d//d//d//d//d//d//d//.//d//d//d//d//d//d//.pom")) {
186             result = true;
187         }
188         return result;
189     }
190 
191     /***
192      * Returns a cached result of list performed on file, if
193      * available. Invokes the method and caches the result otherwise.
194      *
195      * @param dir A directory to list the contents of.
196      * @return The array of files and dirs in the directory.
197      */
198     private String[] list(final File dir) {
199         String[] files = (String[]) fileListMap.get(dir);
200         if (files == null) {
201             files = dir.list();
202             if (files != null) {
203                 fileListMap.put(dir, files);
204             }
205         }
206         return files;
207     }
208 }