35
35
import java .io .IOException ;
36
36
import java .io .InputStream ;
37
37
import java .net .URL ;
38
+ import java .util .ArrayList ;
39
+ import java .util .Comparator ;
40
+ import java .util .Enumeration ;
41
+ import java .util .List ;
38
42
39
43
import javax .xml .parsers .ParserConfigurationException ;
40
44
45
49
*
46
50
* @author Curtis Rueden
47
51
*/
48
- public class POM extends XML {
52
+ public class POM extends XML implements Comparable < POM > {
49
53
50
54
/** Parses a POM from the given file. */
51
55
public POM (final File file ) throws ParserConfigurationException ,
@@ -54,6 +58,13 @@ public POM(final File file) throws ParserConfigurationException,
54
58
super (file );
55
59
}
56
60
61
+ /** Parses a POM from the given URL. */
62
+ public POM (final URL url ) throws ParserConfigurationException , SAXException ,
63
+ IOException
64
+ {
65
+ super (url );
66
+ }
67
+
57
68
/** Parses a POM from the given input stream. */
58
69
public POM (final InputStream in ) throws ParserConfigurationException ,
59
70
SAXException , IOException
@@ -62,8 +73,8 @@ public POM(final InputStream in) throws ParserConfigurationException,
62
73
}
63
74
64
75
/** Parses a POM from the given string. */
65
- public POM (final String s ) throws ParserConfigurationException ,
66
- SAXException , IOException
76
+ public POM (final String s ) throws ParserConfigurationException , SAXException ,
77
+ IOException
67
78
{
68
79
super (s );
69
80
}
@@ -89,6 +100,52 @@ public String getVersion() {
89
100
return cdata ("//project/parent/version" );
90
101
}
91
102
103
+ /** Gets the project name. */
104
+ public String getProjectName () {
105
+ return cdata ("//project/name" );
106
+ }
107
+
108
+ /** Gets the project description. */
109
+ public String getProjectDescription () {
110
+ return cdata ("//project/description" );
111
+ }
112
+
113
+ /** Gets the project URL. */
114
+ public String getProjectURL () {
115
+ return cdata ("//project/url" );
116
+ }
117
+
118
+ /** Gets the project inception year. */
119
+ public String getProjectInceptionYear () {
120
+ return cdata ("//project/inceptionYear" );
121
+ }
122
+
123
+ /** Gets the organization name. */
124
+ public String getOrganizationName () {
125
+ return cdata ("//project/organization/name" );
126
+ }
127
+
128
+ /** Gets the organization URL. */
129
+ public String getOrganizationURL () {
130
+ return cdata ("//project/organization/url" );
131
+ }
132
+
133
+ // -- Comparable methods --
134
+
135
+ @ Override
136
+ public int compareTo (final POM pom ) {
137
+ // sort by groupId first
138
+ final int gid = getGroupId ().compareTo (pom .getGroupId ());
139
+ if (gid != 0 ) return gid ;
140
+
141
+ // sort by artifactId second
142
+ final int aid = getArtifactId ().compareTo (pom .getArtifactId ());
143
+ if (aid != 0 ) return aid ;
144
+
145
+ // finally, sort by version
146
+ return compareVersions (getVersion (), pom .getVersion ());
147
+ }
148
+
92
149
// -- Utility methods --
93
150
94
151
/**
@@ -103,13 +160,15 @@ public static POM getPOM(final Class<?> c, final String groupId,
103
160
{
104
161
try {
105
162
final URL location = ClassUtils .getLocation (c );
106
- if (!location .getProtocol ().equals ("file" ) || location .toString ().endsWith (".jar" )) {
163
+ if (!location .getProtocol ().equals ("file" ) ||
164
+ location .toString ().endsWith (".jar" ))
165
+ {
107
166
// look for pom.xml in JAR's META-INF/maven subdirectory
108
167
final String pomPath =
109
168
"META-INF/maven/" + groupId + "/" + artifactId + "/pom.xml" ;
110
- final URL pomURL = new URL ( "jar:" + location . toString () + "!/" + pomPath );
111
- final InputStream pomStream = pomURL . openStream ( );
112
- return new POM (pomStream );
169
+ final URL pomURL =
170
+ new URL ( "jar:" + location . toString () + "!/" + pomPath );
171
+ return new POM (pomURL );
113
172
}
114
173
// look for the POM in the class's base directory
115
174
final File file = FileUtils .urlToFile (location );
@@ -128,4 +187,107 @@ public static POM getPOM(final Class<?> c, final String groupId,
128
187
}
129
188
}
130
189
190
+ /** Gets all available Maven POMs on the class path. */
191
+ public static List <POM > getAllPOMs () {
192
+ // find all META-INF/maven/ folders on the classpath
193
+ final String pomPrefix = "META-INF/maven/" ;
194
+ final ClassLoader classLoader =
195
+ Thread .currentThread ().getContextClassLoader ();
196
+ final Enumeration <URL > resources ;
197
+ try {
198
+ resources = classLoader .getResources (pomPrefix );
199
+ }
200
+ catch (final IOException exc ) {
201
+ return null ;
202
+ }
203
+
204
+ final ArrayList <POM > poms = new ArrayList <POM >();
205
+
206
+ // recursively list contents of META-INF/maven/ directories
207
+ for (final URL resource : new IteratorPlus <URL >(resources )) {
208
+ for (final URL url : FileUtils .listContents (resource )) {
209
+ // look for pom.xml files amongst the contents
210
+ if (url .getPath ().endsWith ("/pom.xml" )) {
211
+ try {
212
+ poms .add (new POM (url ));
213
+ }
214
+ catch (final IOException exc ) {
215
+ // ignore and continue
216
+ }
217
+ catch (final ParserConfigurationException exc ) {
218
+ // ignore and continue
219
+ }
220
+ catch (final SAXException exc ) {
221
+ // ignore and continue
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ return poms ;
228
+ }
229
+
230
+ /**
231
+ * Compares two version strings.
232
+ * <p>
233
+ * Given the variation between versioning styles, there is no single
234
+ * comparison method that can possibly be correct 100% of the time. So this
235
+ * method works on a "best effort" basis; YMMV.
236
+ * </p>
237
+ * <p>
238
+ * The algorithm is as follows:
239
+ * </p>
240
+ * <ul>
241
+ * <li>Split on non-alphameric characters.</li>
242
+ * <li>Compare each token one by one.</li>
243
+ * <li>Comparison is numerical when possible (i.e., when an integer can be
244
+ * parsed from the token), and lexicographic otherwise.</li>
245
+ * <li>If one version string runs out of tokens, the version with additional
246
+ * tokens remaining is considered <em>greater than</em> the version without
247
+ * additional tokens.</li>
248
+ * <li>There is one exception: if two version strings are identical except
249
+ * that one has a suffix beginning with a dash ({@code -}), the version with
250
+ * suffix will be considered <em>less than</em> the one without a suffix. The
251
+ * reason for this is to accommodate the <a
252
+ * href="http://semver.org/">SemVer</a> versioning scheme's usage of
253
+ * "prerelease" version suffixes. For example, {@code 2.0.0} will compare
254
+ * greater than {@code 2.0.0-beta-1}, whereas {@code 2.0.0} will compare less
255
+ * than {@code 2.0.0.1}.</li>
256
+ * </ul>
257
+ *
258
+ * @return a negative integer, zero, or a positive integer as the first
259
+ * argument is less than, equal to, or greater than the second.
260
+ * @see Comparator#compare(Object, Object)
261
+ */
262
+ public static int compareVersions (final String v1 , final String v2 ) {
263
+ final String [] t1 = v1 .split ("[^\\ w]" );
264
+ final String [] t2 = v2 .split ("[^\\ w]" );
265
+ final int size = Math .min (t1 .length , t2 .length );
266
+ for (int i = 0 ; i < size ; i ++) {
267
+ try {
268
+ final long n1 = Long .parseLong (t1 [i ]);
269
+ final long n2 = Long .parseLong (t2 [i ]);
270
+ if (n1 < n2 ) return -1 ;
271
+ if (n1 > n2 ) return 1 ;
272
+ }
273
+ catch (final NumberFormatException exc ) {
274
+ final int result = t1 [i ].compareTo (t2 [i ]);
275
+ if (result != 0 ) return result ;
276
+ }
277
+ }
278
+ if (t1 .length == t2 .length ) return 0 ; // versions match
279
+
280
+ // check for SemVer prerelease versions
281
+ if (v1 .startsWith (v2 ) && v1 .charAt (v2 .length ()) == '-' ) {
282
+ // v1 is a prerelease version of v2
283
+ return -1 ;
284
+ }
285
+ if (v2 .startsWith (v1 ) && v2 .charAt (v1 .length ()) == '-' ) {
286
+ // v2 is a prerelease version of v1
287
+ return 1 ;
288
+ }
289
+
290
+ return t1 .length < t2 .length ? -1 : 1 ;
291
+ }
292
+
131
293
}
0 commit comments