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 }