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 }