diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 2934a79a4f9ec629b4bb267c172187054cc8ef7f..b7725aa68fcba854bb08034d77412e07cee83bd1 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -73,6 +73,9 @@ import hudson.util.jna.Kernel32Utils; import static hudson.util.jna.GNUCLibrary.LIBC; import java.security.DigestInputStream; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.apache.commons.codec.digest.DigestUtils; /** @@ -94,7 +97,8 @@ public class Util { * Creates a filtered sublist. * @since 1.176 */ - public static List filter( Iterable base, Class type ) { + @Nonnull + public static List filter( @Nonnull Iterable base, @Nonnull Class type ) { List r = new ArrayList(); for (Object i : base) { if(type.isInstance(i)) @@ -106,7 +110,8 @@ public class Util { /** * Creates a filtered sublist. */ - public static List filter( List base, Class type ) { + @Nonnull + public static List filter( @Nonnull List base, @Nonnull Class type ) { return filter((Iterable)base,type); } @@ -122,7 +127,8 @@ public class Util { * Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.) * */ - public static String replaceMacro(String s, Map properties) { + @Nullable + public static String replaceMacro( @CheckForNull String s, @Nonnull Map properties) { return replaceMacro(s,new VariableResolver.ByMap(properties)); } @@ -132,7 +138,8 @@ public class Util { *

* Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.) */ - public static String replaceMacro(String s, VariableResolver resolver) { + @Nullable + public static String replaceMacro(@CheckForNull String s, @Nonnull VariableResolver resolver) { if (s == null) { return null; } @@ -165,11 +172,13 @@ public class Util { /** * Loads the contents of a file into a string. */ - public static String loadFile(File logfile) throws IOException { + @Nonnull + public static String loadFile(@Nonnull File logfile) throws IOException { return loadFile(logfile, Charset.defaultCharset()); } - public static String loadFile(File logfile,Charset charset) throws IOException { + @Nonnull + public static String loadFile(@Nonnull File logfile, @Nonnull Charset charset) throws IOException { if(!logfile.exists()) return ""; @@ -195,7 +204,7 @@ public class Util { * @throws IOException * if the operation fails. */ - public static void deleteContentsRecursive(File file) throws IOException { + public static void deleteContentsRecursive(@Nonnull File file) throws IOException { File[] files = file.listFiles(); if(files==null) return; // the directory didn't exist in the first place @@ -208,7 +217,7 @@ public class Util { * @param f a file to delete * @throws IOException if it exists but could not be successfully deleted */ - public static void deleteFile(File f) throws IOException { + public static void deleteFile(@Nonnull File f) throws IOException { if (!f.delete()) { if(!f.exists()) // we are trying to delete a file that no longer exists, so this is not an error @@ -259,7 +268,7 @@ public class Util { /** * Makes the given file writable by any means possible. */ - private static void makeWritable(File f) { + private static void makeWritable(@Nonnull File f) { if (f.setWritable(true)) { return; } @@ -287,7 +296,7 @@ public class Util { } - public static void deleteRecursive(File dir) throws IOException { + public static void deleteRecursive(@Nonnull File dir) throws IOException { if(!isSymlink(dir)) deleteContentsRecursive(dir); try { @@ -321,7 +330,7 @@ public class Util { * Checks if the given file represents a symlink. */ //Taken from http://svn.apache.org/viewvc/maven/shared/trunk/file-management/src/main/java/org/apache/maven/shared/model/fileset/util/FileSetManager.java?view=markup - public static boolean isSymlink(File file) throws IOException { + public static boolean isSymlink(@Nonnull File file) throws IOException { Boolean r = isSymlinkJava7(file); if (r != null) { return r; @@ -350,7 +359,7 @@ public class Util { } @SuppressWarnings("NP_BOOLEAN_RETURN_NULL") - private static Boolean isSymlinkJava7(File file) throws IOException { + private static Boolean isSymlinkJava7(@Nonnull File file) throws IOException { try { Object path = File.class.getMethod("toPath").invoke(file); return (Boolean) Class.forName("java.nio.file.Files").getMethod("isSymbolicLink", Class.forName("java.nio.file.Path")).invoke(null, path); @@ -379,13 +388,14 @@ public class Util { * On Windows, error messages for IOException aren't very helpful. * This method generates additional user-friendly error message to the listener */ - public static void displayIOException( IOException e, TaskListener listener ) { + public static void displayIOException(@Nonnull IOException e, @Nonnull TaskListener listener ) { String msg = getWin32ErrorMessage(e); if(msg!=null) listener.getLogger().println(msg); } - public static String getWin32ErrorMessage(IOException e) { + @CheckForNull + public static String getWin32ErrorMessage(@Nonnull IOException e) { return getWin32ErrorMessage((Throwable)e); } @@ -395,6 +405,7 @@ public class Util { * @return * null if there seems to be no error code or if the platform is not Win32. */ + @CheckForNull public static String getWin32ErrorMessage(Throwable e) { String msg = e.getMessage(); if(msg!=null) { @@ -420,6 +431,7 @@ public class Util { * @return * null if no such message is available. */ + @CheckForNull public static String getWin32ErrorMessage(int n) { try { ResourceBundle rb = ResourceBundle.getBundle("/hudson/win32errors"); @@ -433,6 +445,7 @@ public class Util { /** * Guesses the current host name. */ + @Nonnull public static String getHostName() { try { return InetAddress.getLocalHost().getHostName(); @@ -441,21 +454,21 @@ public class Util { } } - public static void copyStream(InputStream in,OutputStream out) throws IOException { + public static void copyStream(@Nonnull InputStream in,@Nonnull OutputStream out) throws IOException { byte[] buf = new byte[8192]; int len; while((len=in.read(buf))>=0) out.write(buf,0,len); } - public static void copyStream(Reader in, Writer out) throws IOException { + public static void copyStream(@Nonnull Reader in, @Nonnull Writer out) throws IOException { char[] buf = new char[8192]; int len; while((len=in.read(buf))>0) out.write(buf,0,len); } - public static void copyStreamAndClose(InputStream in,OutputStream out) throws IOException { + public static void copyStreamAndClose(@Nonnull InputStream in, @Nonnull OutputStream out) throws IOException { try { copyStream(in,out); } finally { @@ -464,7 +477,7 @@ public class Util { } } - public static void copyStreamAndClose(Reader in,Writer out) throws IOException { + public static void copyStreamAndClose(@Nonnull Reader in, @Nonnull Writer out) throws IOException { try { copyStream(in,out); } finally { @@ -483,18 +496,21 @@ public class Util { * @since 1.145 * @see QuotedStringTokenizer */ - public static String[] tokenize(String s,String delimiter) { + @Nonnull + public static String[] tokenize(@Nonnull String s, @CheckForNull String delimiter) { return QuotedStringTokenizer.tokenize(s,delimiter); } - public static String[] tokenize(String s) { + @Nonnull + public static String[] tokenize(@Nonnull String s) { return tokenize(s," \t\n\r\f"); } /** * Converts the map format of the environment variables to the K=V format in the array. */ - public static String[] mapToEnv(Map m) { + @Nonnull + public static String[] mapToEnv(@Nonnull Map m) { String[] r = new String[m.size()]; int idx=0; @@ -504,7 +520,7 @@ public class Util { return r; } - public static int min(int x, int... values) { + public static int min(int x, @Nonnull int... values) { for (int i : values) { if(i List createSubList( Collection source, Class type ) { + @Nonnull + public static List createSubList(@Nonnull Collection source, @Nonnull Class type ) { List r = new ArrayList(); for (Object item : source) { if(type.isInstance(item)) @@ -749,7 +780,8 @@ public class Util { * {@link #rawEncode(String)} should generally be used instead, though be careful to pass only * a single path component to that method (it will encode /, but this method does not). */ - public static String encode(String s) { + @Nonnull + public static String encode(@Nonnull String s) { try { boolean escaped = false; @@ -806,7 +838,8 @@ public class Util { * single path component used in constructing a URL. * Method name inspired by PHP's rawurlencode. */ - public static String rawEncode(String s) { + @Nonnull + public static String rawEncode(@Nonnull String s) { boolean escaped = false; StringBuilder out = null; CharsetEncoder enc = null; @@ -855,7 +888,8 @@ public class Util { /** * Escapes HTML unsafe characters like <, & to the respective character entities. */ - public static String escape(String text) { + @Nonnull + public static String escape(@Nonnull String text) { if (text==null) return null; StringBuilder buf = new StringBuilder(text.length()+64); for( int i=0; i List fixNull(List l) { + @Nonnull + public static List fixNull(@CheckForNull List l) { return l!=null ? l : Collections.emptyList(); } - public static Set fixNull(Set l) { + @Nonnull + public static Set fixNull(@CheckForNull Set l) { return l!=null ? l : Collections.emptySet(); } - public static Collection fixNull(Collection l) { + @Nonnull + public static Collection fixNull(@CheckForNull Collection l) { return l!=null ? l : Collections.emptySet(); } - public static Iterable fixNull(Iterable l) { + @Nonnull + public static Iterable fixNull(@CheckForNull Iterable l) { return l!=null ? l : Collections.emptySet(); } /** * Cuts all the leading path portion and get just the file name. */ - public static String getFileName(String filePath) { + @Nonnull + public static String getFileName(@Nonnull String filePath) { int idx = filePath.lastIndexOf('\\'); if(idx>=0) return getFileName(filePath.substring(idx+1)); @@ -980,7 +1023,8 @@ public class Util { /** * Concatenate multiple strings by inserting a separator. */ - public static String join(Collection strings, String separator) { + @Nonnull + public static String join(@Nonnull Collection strings, @Nonnull String separator) { StringBuilder buf = new StringBuilder(); boolean first=true; for (Object s : strings) { @@ -994,7 +1038,8 @@ public class Util { /** * Combines all the given collections into a single list. */ - public static List join(Collection... items) { + @Nonnull + public static List join(@Nonnull Collection... items) { int size = 0; for (Collection item : items) size += item.size(); @@ -1021,7 +1066,8 @@ public class Util { * Can be null. * @since 1.172 */ - public static FileSet createFileSet(File baseDir, String includes, String excludes) { + @Nonnull + public static FileSet createFileSet(@Nonnull File baseDir, @Nonnull String includes, @CheckForNull String excludes) { FileSet fs = new FileSet(); fs.setDir(baseDir); fs.setProject(new Project()); @@ -1043,7 +1089,8 @@ public class Util { return fs; } - public static FileSet createFileSet(File baseDir, String includes) { + @Nonnull + public static FileSet createFileSet(@Nonnull File baseDir, @Nonnull String includes) { return createFileSet(baseDir,includes,null); } @@ -1059,7 +1106,8 @@ public class Util { * @param symlinkPath * Where to create a symlink in (relative to {@code baseDir}) */ - public static void createSymlink(File baseDir, String targetPath, String symlinkPath, TaskListener listener) throws InterruptedException { + public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath, + @Nonnull String symlinkPath, @Nonnull TaskListener listener) throws InterruptedException { try { if (createSymlinkJava7(baseDir, targetPath, symlinkPath)) { return; @@ -1131,7 +1179,7 @@ public class Util { } } - private static boolean createSymlinkJava7(File baseDir, String targetPath, String symlinkPath) throws IOException { + private static boolean createSymlinkJava7(@Nonnull File baseDir, @Nonnull String targetPath, @Nonnull String symlinkPath) throws IOException { try { Object path = File.class.getMethod("toPath").invoke(new File(baseDir, symlinkPath)); Object target = Class.forName("java.nio.file.Paths").getMethod("get", String.class, String[].class).invoke(null, targetPath, new String[0]); @@ -1201,7 +1249,8 @@ public class Util { * @return null * if the specified file is not a symlink. */ - public static File resolveSymlinkToFile(File link) throws InterruptedException, IOException { + @CheckForNull + public static File resolveSymlinkToFile(@Nonnull File link) throws InterruptedException, IOException { String target = resolveSymlink(link); if (target==null) return null; @@ -1221,7 +1270,8 @@ public class Util { * If the symlink is relative, the returned string is that relative representation. * The relative path is meant to be resolved from the location of the symlink. */ - public static String resolveSymlink(File link) throws InterruptedException, IOException { + @CheckForNull + public static String resolveSymlink(@Nonnull File link) throws InterruptedException, IOException { try { // Java 7 Object path = File.class.getMethod("toPath").invoke(link); return Class.forName("java.nio.file.Files").getMethod("readSymbolicLink", Class.forName("java.nio.file.Path")).invoke(null, path).toString(); @@ -1306,7 +1356,8 @@ public class Util { * Wraps with the error icon and the CSS class to render error message. * @since 1.173 */ - public static String wrapToErrorSpan(String s) { + @Nonnull + public static String wrapToErrorSpan(@Nonnull String s) { s = ""+s+""; return s; } @@ -1319,7 +1370,8 @@ public class Util { * @param defaultNumber number to return if the string can not be parsed * @return returns the parsed string; otherwise the default number */ - public static Number tryParseNumber(String numberStr, Number defaultNumber) { + @CheckForNull + public static Number tryParseNumber(@CheckForNull String numberStr, @CheckForNull Number defaultNumber) { if ((numberStr == null) || (numberStr.length() == 0)) { return defaultNumber; } @@ -1334,7 +1386,7 @@ public class Util { * Checks if the public method defined on the base type with the given arguments * are overridden in the given derived type. */ - public static boolean isOverridden(Class base, Class derived, String methodName, Class... types) { + public static boolean isOverridden(@Nonnull Class base, @Nonnull Class derived, @Nonnull String methodName, @Nonnull Class... types) { try { return !base.getMethod(methodName, types).equals( derived.getMethod(methodName,types)); @@ -1349,7 +1401,8 @@ public class Util { * @param ext * For example, ".zip" */ - public static File changeExtension(File dst, String ext) { + @Nonnull + public static File changeExtension(@Nonnull File dst, @Nonnull String ext) { String p = dst.getPath(); int pos = p.lastIndexOf('.'); if (pos<0) return new File(p+ext); @@ -1358,8 +1411,10 @@ public class Util { /** * Null-safe String intern method. + * @return A canonical representation for the string object. Null for null input strings */ - public static String intern(String s) { + @Nullable + public static String intern(@CheckForNull String s) { return s==null ? s : s.intern(); } @@ -1370,7 +1425,7 @@ public class Util { * implementing this by ourselves allow it to be more lenient about * escaping of URI. */ - public static boolean isAbsoluteUri(String uri) { + public static boolean isAbsoluteUri(@Nonnull String uri) { int idx = uri.indexOf(':'); if (idx<0) return false; // no ':'. can't be absolute @@ -1382,7 +1437,7 @@ public class Util { * Works like {@link String#indexOf(int)} but 'not found' is returned as s.length(), not -1. * This enables more straight-forward comparison. */ - private static int _indexOf(String s, char ch) { + private static int _indexOf(@Nonnull String s, char ch) { int idx = s.indexOf(ch); if (idx<0) return s.length(); return idx; @@ -1392,7 +1447,8 @@ public class Util { * Loads a key/value pair string as {@link Properties} * @since 1.392 */ - public static Properties loadProperties(String properties) throws IOException { + @Nonnull + public static Properties loadProperties(@Nonnull String properties) throws IOException { Properties p = new Properties(); p.load(new StringReader(properties)); return p;