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());
}