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.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import org.jdom.Document;
13 import org.jdom.Element;
14 import org.jdom.xpath.XPath;
15
16 /***
17 * Scans a given path and group id for all POMs of this group and returns
18 * all artifact ids of that group available there.
19 *
20 * @author Matthias Burbach
21 */
22 public class ArtifactScanner {
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 Map fileListMap = new HashMap();
29
30 /***
31 * The cache for lists of artifact ids that have been returned already,
32 * keyed by the group directories.
33 */
34 private static Map resultCache = new HashMap();
35
36 /***
37 * Clears the internal cache of already scanned artifacts.
38 * The cache is shared by all instances of this class.
39 */
40 public static void clearCache() {
41 resultCache = new HashMap();
42 }
43
44 /***
45 * Scans a given group directory for all artifact ids of the group.<br/>
46 * Returns the result from a local cache if possible.
47 *
48 * @param groupDir The absolute file system path of a group directory to
49 * scan for all artifact ids of the group.
50 * @return The list of artifact ids of type {@link String}.
51 * Is never <code>null</code>.
52 */
53 public List getAllArtifactIds(final String groupDir) {
54 final String key = groupDir;
55 List result = (List) resultCache.get(key);
56 if (result == null) {
57 result = doGetAllArtifactIds(groupDir);
58 if (result != null) {
59 Collections.sort(result);
60 resultCache.put(key, result);
61 }
62 }
63 return result;
64 }
65
66 /***
67 * Scans a given group directory for all artifact ids of the group.<br/>
68 *
69 * @param groupDir The absolute file system path of a group directory to
70 * scan for all artifact ids of the group.
71 * @return The list of artifact ids of type {@link String}.
72 * Is never <code>null</code>.
73 */
74 private List doGetAllArtifactIds(final String groupDir) {
75 Set result = new HashSet();
76 File pomsDir = new File(groupDir + File.separator + "poms");
77 if (pomsDir.isDirectory()) {
78 String[] fileNames = list(pomsDir);
79 for (int i = 0; i < fileNames.length; i++) {
80 String fileName = fileNames[i];
81 if (!isExcludedFileName(fileName)) {
82 String artifactId =
83 extractArtifactId(pomsDir.getAbsolutePath(), fileName);
84 if (artifactId != null
85 && fileName.startsWith(artifactId)) {
86 result.add(artifactId);
87 }
88 }
89 }
90 }
91 return new ArrayList(result);
92 }
93
94 /***
95 * Loads and parses the POM XML document and returns the artifact id.
96 *
97 * @param path The absolute file system path of the file to parse
98 * without the file name.
99 * @param fileName The simple name of the file to parse.
100 * @return The artifact id parsed or null in case of any failure.
101 */
102 private String extractArtifactId(final String path, final String fileName) {
103 String result = null;
104 if (fileName.startsWith("vrp-") && fileName.indexOf("SNAPSHOT") == -1) {
105 result = extractArtifactIdFromFileName(fileName);
106 } else {
107 Document document = PomLoader.getInstance().loadPom(
108 path + File.separator + fileName);
109 try {
110 result = parseElementText(document, "project/artifactId");
111 } catch (Exception e) {
112 e.printStackTrace();
113 }
114 }
115 return result;
116 }
117
118 /***
119 * Optimizes overall performance by assuming that the file name is composed
120 * as artifactId-version.pom and that the version does not contain a '-'.
121 *
122 * @param fileName The file name to extract the artifact id from.
123 * @return The artifact id extracted.
124 */
125 private String extractArtifactIdFromFileName(final String fileName) {
126 int artifactEndIndex = fileName.lastIndexOf("-");
127 String result = fileName.substring(0, artifactEndIndex);
128 return result;
129 }
130
131 /***
132 * Tries to parse the element named <code>elementName.</code>.
133 *
134 * @param xmlNode The XML node to that contains the element to parse.
135 * @param elementName The name of the element to parse.
136 * @return The contents of the element to parse or <code>null</code>.
137 * @throws Exception if anything goes unexpectedly wrong
138 */
139 private String parseElementText(
140 final Object xmlNode,
141 final String elementName)
142 throws Exception {
143 Element element =
144 (Element) XPath.newInstance(elementName).selectSingleNode(xmlNode);
145 String result = null;
146 if (element != null) {
147 result = element.getText();
148 }
149 return result;
150 }
151
152 /***
153 * @param fileName The simple name of the file to check.
154 * @return <code>true</code> if the fileName is to be excluded from the
155 * artifact id scanning
156 */
157 private boolean isExcludedFileName(final String fileName) {
158 boolean result = true;
159 String candidate = fileName.toLowerCase();
160 if (candidate.endsWith(".pom")
161 && !candidate.endsWith("openedition.pom")
162 && candidate.indexOf("snapshot-") == -1) {
163 result = false;
164 }
165 return result;
166 }
167
168 /***
169 * Returns a cached result of list performed on file, if
170 * available. Invokes the method and caches the result otherwise.
171 *
172 * @param dir A directory to list the contents of.
173 * @return The array of files and dirs in the directory.
174 */
175 private String[] list(final File dir) {
176 String[] files = (String[]) fileListMap.get(dir);
177 if (files == null) {
178 files = dir.list();
179 if (files != null) {
180 fileListMap.put(dir, files);
181 }
182 }
183 return files;
184 }
185 }