package hudson; import hudson.model.BuildListener; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.StringTokenizer; import java.util.SimpleTimeZone; import java.util.logging.Logger; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.text.SimpleDateFormat; import org.apache.tools.ant.taskdefs.Chmod; import org.apache.tools.ant.taskdefs.Copy; import org.apache.tools.ant.BuildException; /** * @author Kohsuke Kawaguchi */ public class Util { /** * Replaces the occurrence of '$key' by properties.get('key'). * *

* This is a rather naive implementation that causes somewhat unexpected * behavior when the expansion contains other macros. */ public static String replaceMacro(String s, Map properties) { int idx=0; while((idx=s.indexOf('$',idx))>=0) { // identify the key int end=idx+1; while(end0) str.append(buf,0,len); r.close(); return str.toString(); } /** * Deletes the contents of the given directory (but not the directory itself) * recursively. * * @throws IOException * if the operation fails. */ public static void deleteContentsRecursive(File file) throws IOException { File[] files = file.listFiles(); if(files==null) return; // the directory didn't exist in the first place for (File child : files) { if (child.isDirectory()) deleteContentsRecursive(child); deleteFile(child); } } private static void deleteFile(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 return; // perhaps this file is read-only? // try chmod. this becomes no-op if this is not Unix. try { Chmod chmod = new Chmod(); chmod.setProject(new org.apache.tools.ant.Project()); chmod.setFile(f); chmod.setPerm("u+w"); chmod.execute(); } catch (BuildException e) { LOGGER.log(Level.INFO,"Failed to chmod "+f,e); } throw new IOException("Unable to delete " + f.getPath()); } } public static void deleteRecursive(File dir) throws IOException { deleteContentsRecursive(dir); deleteFile(dir); } /** * Creates a new temporary directory. */ public static File createTempDir() throws IOException { File tmp = File.createTempFile("hudson", "tmp"); if(!tmp.delete()) throw new IOException("Failed to delete "+tmp); if(!tmp.mkdirs()) throw new IOException("Failed to create a new directory "+tmp); return tmp; } private static final Pattern errorCodeParser = Pattern.compile(".*error=([0-9]+).*"); /** * 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, BuildListener listener ) { if(File.separatorChar!='\\') return; // not Windows Matcher m = errorCodeParser.matcher(e.getMessage()); if(!m.matches()) return; // failed to parse try { ResourceBundle rb = ResourceBundle.getBundle("/hudson/win32errors"); listener.getLogger().println(rb.getString("error"+m.group(1))); } catch (Exception _) { // silently recover from resource related failures } } /** * Guesses the current host name. */ public static String getHostName() { try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { return "localhost"; } } public static void copyStream(InputStream in,OutputStream out) throws IOException { byte[] buf = new byte[8192]; int len; while((len=in.read(buf))>0) out.write(buf,0,len); } public static String[] tokenize(String s) { StringTokenizer st = new StringTokenizer(s); String[] a = new String[st.countTokens()]; for (int i = 0; st.hasMoreTokens(); i++) a[i] = st.nextToken(); return a; } public static String[] mapToEnv(Map m) { String[] r = new String[m.size()]; int idx=0; for (final Map.Entry e : m.entrySet()) { r[idx++] = e.getKey() + '=' + e.getValue(); } return r; } public static int min(int x, int... values) { for (int i : values) { if(i UTF8 w.write(c); w.flush(); for (byte b : buf.toByteArray()) { out.append('%'); out.append(toDigit((b >> 4) & 0xF)); out.append(toDigit(b & 0xF)); } buf.reset(); escaped = true; } } return escaped ? out.toString() : s; } catch (IOException e) { throw new Error(e); // impossible } } private static char toDigit(int n) { char ch = Character.forDigit(n,16); if(ch>='a') ch = (char)(ch-'a'+'A'); return ch; } /** * Creates an empty file. */ public static void touch(File file) throws IOException { new FileOutputStream(file).close(); } /** * Copies a single file by using Ant. */ public static void copyFile(File src, File dst) throws BuildException { Copy cp = new Copy(); cp.setProject(new org.apache.tools.ant.Project()); cp.setTofile(dst); cp.setFile(src); cp.setOverwrite(true); cp.execute(); } /** * Convert null to "". */ public static String fixNull(String s) { if(s==null) return ""; else return s; } /** * Convert empty string to null. */ public static String fixEmpty(String s) { if(s==null || s.length()==0) return null; return s; } /** * Cuts all the leading path portion and get just the file name. */ public static String getFileName(String filePath) { int idx = filePath.lastIndexOf('\\'); if(idx>=0) return getFileName(filePath.substring(idx+1)); idx = filePath.lastIndexOf('/'); if(idx>=0) return getFileName(filePath.substring(idx+1)); return filePath; } public static final SimpleDateFormat XS_DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // Note: RFC822 dates must not be localized! public static final SimpleDateFormat RFC822_DATETIME_FORMATTER = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); static { XS_DATETIME_FORMATTER.setTimeZone(new SimpleTimeZone(0,"GMT")); } private static final Logger LOGGER = Logger.getLogger(Util.class.getName()); }