1 package com.fasterxml.jackson.core.util;
2 
3 import java.io.*;
4 import java.util.Properties;
5 import java.util.regex.Pattern;
6 
7 import com.fasterxml.jackson.core.Version;
8 import com.fasterxml.jackson.core.Versioned;
9 
10 /**
11  * Functionality for supporting exposing of component {@link Version}s.
12  * Also contains other misc methods that have no other place to live in.
13  *<p>
14  * Note that this class can be used in two roles: first, as a static
15  * utility class for loading purposes, and second, as a singleton
16  * loader of per-module version information.
17  *<p>
18  * Note that method for accessing version information changed between versions
19  * 2.1 and 2.2; earlier code used file named "VERSION.txt"; but this has serious
20  * performance issues on some platforms (Android), so a replacement system
21  * was implemented to use class generation and dynamic class loading.
22  *<p>
23  * Note that functionality for reading "VERSION.txt" was removed completely
24  * from Jackson 2.6.
25  */
26 public class VersionUtil
27 {
28     private final static Pattern V_SEP = Pattern.compile("[-_./;:]");
29 
30     /*
31     /**********************************************************
32     /* Instance life-cycle
33     /**********************************************************
34      */
35 
VersionUtil()36     protected VersionUtil() { }
37 
38     @Deprecated // since 2.9
version()39     public Version version() { return Version.unknownVersion(); }
40 
41     /*
42     /**********************************************************
43     /* Static load methods
44     /**********************************************************
45      */
46 
47     /**
48      * Helper method that will try to load version information for specified
49      * class. Implementation is as follows:
50      *
51      * First, tries to load version info from a class named
52      * "PackageVersion" in the same package as the class.
53      *
54      * If no version information is found, {@link Version#unknownVersion()} is returned.
55      */
versionFor(Class<?> cls)56     public static Version versionFor(Class<?> cls)
57     {
58         Version version = packageVersionFor(cls);
59         return version == null ? Version.unknownVersion() : version;
60     }
61 
62     /**
63      * Loads version information by introspecting a class named
64      * "PackageVersion" in the same package as the given class.
65      *<p>
66      * If the class could not be found or does not have a public
67      * static Version field named "VERSION", returns null.
68      */
packageVersionFor(Class<?> cls)69     public static Version packageVersionFor(Class<?> cls)
70     {
71         Version v = null;
72         try {
73             String versionInfoClassName = cls.getPackage().getName() + ".PackageVersion";
74             Class<?> vClass = Class.forName(versionInfoClassName, true, cls.getClassLoader());
75             // However, if class exists, it better work correctly, no swallowing exceptions
76             try {
77                 v = ((Versioned) vClass.getDeclaredConstructor().newInstance()).version();
78             } catch (Exception e) {
79                 throw new IllegalArgumentException("Failed to get Versioned out of "+vClass);
80             }
81         } catch (Exception e) { // ok to be missing (not good but acceptable)
82             ;
83         }
84         return (v == null) ? Version.unknownVersion() : v;
85     }
86 
87     /**
88      * Will attempt to load the maven version for the given groupId and
89      * artifactId.  Maven puts a pom.properties file in
90      * META-INF/maven/groupId/artifactId, containing the groupId,
91      * artifactId and version of the library.
92      *
93      * @param cl the ClassLoader to load the pom.properties file from
94      * @param groupId the groupId of the library
95      * @param artifactId the artifactId of the library
96      * @return The version
97      *
98      * @deprecated Since 2.6: functionality not used by any official Jackson component, should be
99      *   moved out if anyone needs it
100      */
101     @SuppressWarnings("resource")
102     @Deprecated // since 2.6
mavenVersionFor(ClassLoader cl, String groupId, String artifactId)103     public static Version mavenVersionFor(ClassLoader cl, String groupId, String artifactId)
104     {
105         InputStream pomProperties = cl.getResourceAsStream("META-INF/maven/"
106                 + groupId.replaceAll("\\.", "/")+ "/" + artifactId + "/pom.properties");
107         if (pomProperties != null) {
108             try {
109                 Properties props = new Properties();
110                 props.load(pomProperties);
111                 String versionStr = props.getProperty("version");
112                 String pomPropertiesArtifactId = props.getProperty("artifactId");
113                 String pomPropertiesGroupId = props.getProperty("groupId");
114                 return parseVersion(versionStr, pomPropertiesGroupId, pomPropertiesArtifactId);
115             } catch (IOException e) {
116                 // Ignore
117             } finally {
118                 _close(pomProperties);
119             }
120         }
121         return Version.unknownVersion();
122     }
123 
124     /**
125      * Method used by <code>PackageVersion</code> classes to decode version injected by Maven build.
126      */
parseVersion(String s, String groupId, String artifactId)127     public static Version parseVersion(String s, String groupId, String artifactId)
128     {
129         if (s != null && (s = s.trim()).length() > 0) {
130             String[] parts = V_SEP.split(s);
131             return new Version(parseVersionPart(parts[0]),
132                     (parts.length > 1) ? parseVersionPart(parts[1]) : 0,
133                     (parts.length > 2) ? parseVersionPart(parts[2]) : 0,
134                     (parts.length > 3) ? parts[3] : null,
135                     groupId, artifactId);
136         }
137         return Version.unknownVersion();
138     }
139 
parseVersionPart(String s)140     protected static int parseVersionPart(String s) {
141         int number = 0;
142         for (int i = 0, len = s.length(); i < len; ++i) {
143             char c = s.charAt(i);
144             if (c > '9' || c < '0') break;
145             number = (number * 10) + (c - '0');
146         }
147         return number;
148     }
149 
_close(Closeable c)150     private final static void _close(Closeable c) {
151         try {
152             c.close();
153         } catch (IOException e) { }
154     }
155 
156     /*
157     /**********************************************************
158     /* Orphan utility methods
159     /**********************************************************
160      */
161 
throwInternal()162     public final static void throwInternal() {
163         throw new RuntimeException("Internal error: this code path should never get executed");
164     }
165 }
166