Index: /trunk/.classpath
===================================================================
--- /trunk/.classpath	(revision 401)
+++ /trunk/.classpath	(revision 402)
@@ -2,10 +2,10 @@
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry excluding="build/|dist/|src/|test/" including="images/" kind="src" path=""/>
+	<classpathentry excluding="build/|dist/|src/|test/" including="images/|presets/" kind="src" path=""/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="lib" path="lib/metadata-extractor-2.3.1-nosun.jar"/>
-	<classpathentry kind="lib" path="lib/gettext-commons-0.9.jar" />
+	<classpathentry kind="lib" path="lib/gettext-commons-0.9.jar"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
-	<classpathentry kind="lib" path="lib/jfcunit.jar" />
+	<classpathentry kind="lib" path="lib/jfcunit.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
Index: /trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 401)
+++ /trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 402)
@@ -54,8 +54,14 @@
 	private Point mousePos;
 	private SelectionManager selectionManager;
-
-	/**
-	 * Create a new MoveAction
-	 * @param mapFrame The MapFrame, this action belongs to.
+	
+	/**
+	 * The time which needs to pass between click and release before something
+	 * counts as a move
+	 */
+	private int initialMoveDelay = 100;
+
+	/**
+	 * Create a new SelectAction
+	 * @param mapFrame The MapFrame this action belongs to.
 	 */
 	public SelectAction(MapFrame mapFrame) {
@@ -64,5 +70,8 @@
 			getCursor("normal", "selection", Cursor.DEFAULT_CURSOR));
 		putValue("help", "Action/Move/Move");
-		selectionManager = new SelectionManager(this, false, mapFrame.mapView);
+		selectionManager = new SelectionManager(this, false, mapFrame.mapView);		
+		try { initialMoveDelay = Integer.parseInt(Main.pref.get("edit.initial-move-delay","100")); } catch (NumberFormatException x) {};
+
+
 	}
 
@@ -114,5 +123,5 @@
 		
 		// do not count anything as a move if it lasts less than 100 milliseconds.
-		if ((mode == Mode.move) && (System.currentTimeMillis() - mouseDownTime < 100)) return;
+		if ((mode == Mode.move) && (System.currentTimeMillis() - mouseDownTime < initialMoveDelay)) return;
 
 		if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0)
Index: /trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(revision 401)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(revision 402)
@@ -5,4 +5,5 @@
 import static org.openstreetmap.josm.tools.I18n.trn;
 
+import java.awt.CheckboxGroup;
 import java.awt.Color;
 import java.awt.Component;
@@ -12,5 +13,14 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
 import java.util.Collection;
 import java.util.LinkedList;
@@ -20,4 +30,5 @@
 import javax.swing.ButtonGroup;
 import javax.swing.Icon;
+import javax.swing.JCheckBox;
 import javax.swing.JColorChooser;
 import javax.swing.JFileChooser;
@@ -28,4 +39,5 @@
 import javax.swing.JRadioButton;
 import javax.swing.JSeparator;
+import javax.swing.JTextField;
 import javax.swing.filechooser.FileFilter;
 
@@ -43,4 +55,6 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.io.MultiPartFormOutputStream;
+import org.openstreetmap.josm.io.OsmWriter;
 import org.openstreetmap.josm.tools.ColorHelper;
 import org.openstreetmap.josm.tools.DontShowAgainInfo;
@@ -79,4 +93,111 @@
 			Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", RawGpsLayer.this.name), null));
 			Main.main.removeLayer(RawGpsLayer.this);
+		}
+	}
+	
+	public class UploadTraceAction extends AbstractAction {
+		public UploadTraceAction() {
+			super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));
+		}
+		public void actionPerformed(ActionEvent e) {
+			JPanel msg = new JPanel(new GridBagLayout());
+			msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+
+					              "use with care and check if it works as expected.</html>")), GBC.eop());
+			ButtonGroup bg = new ButtonGroup();
+			JRadioButton c1 = null;
+			JRadioButton c2 = null;
+			
+			if (associatedFile != null) {
+				c1 = new JRadioButton(tr("Upload track filtered by JOSM"), false);
+				c2 = new JRadioButton(tr("Upload raw file: {0}", associatedFile.getName()), true);
+			}
+			else
+			{
+				c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);
+				c2 = new JRadioButton(tr("Upload raw file: "), false);
+				c2.setEnabled(false);
+			}
+			c1.setEnabled(false);
+			bg.add(c1);
+			bg.add(c2);
+
+			msg.add(c1, GBC.eol());
+			msg.add(c2, GBC.eop());
+
+			
+			JTextField description = new JTextField();
+			JTextField tags = new JTextField();
+			msg.add(new JLabel(tr("Description:")), GBC.std());
+			msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));
+			msg.add(new JLabel(tr("Tags:")), GBC.std());
+			msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));
+			JCheckBox c3 = new JCheckBox("public");
+			msg.add(c3, GBC.eop());
+			msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());
+			
+			int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);
+			if (answer == JOptionPane.OK_OPTION)
+			{
+				try {
+					String version = Main.pref.get("osm-server.version", "0.5");
+					URL url = new URL(Main.pref.get("osm-server.url") +
+							"/" + version + "/gpx/create");
+
+					// create a boundary string
+					String boundary = MultiPartFormOutputStream.createBoundary();
+					URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
+					urlConn.setRequestProperty("Accept", "*/*");
+					urlConn.setRequestProperty("Content-Type", 
+						MultiPartFormOutputStream.getContentType(boundary));
+					// set some other request headers...
+					urlConn.setRequestProperty("Connection", "Keep-Alive");
+					urlConn.setRequestProperty("Cache-Control", "no-cache");
+					// no need to connect cuz getOutputStream() does it
+					MultiPartFormOutputStream out = 
+						new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
+					out.writeField("description", description.getText());
+					out.writeField("tags", tags.getText());
+					out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");
+					// upload a file
+					out.writeFile("gpx_file", "text/xml", associatedFile);
+					// can also write bytes directly
+					// out.writeFile("myFile", "text/plain", "C:\\test.txt", 
+					// "This is some file text.".getBytes("ASCII"));
+					out.close();
+					// read response from server
+					BufferedReader in = new BufferedReader(
+						new InputStreamReader(urlConn.getInputStream()));
+					String line = "";
+					while((line = in.readLine()) != null) {
+						 System.out.println(line);
+					}
+					in.close();
+					
+					/*
+					int retCode = activeConnection.getResponseCode();
+					System.out.println("got return: "+retCode);
+					String retMsg = activeConnection.getResponseMessage();
+					activeConnection.disconnect();
+					if (retCode != 200) {
+						// Look for a detailed error message from the server
+						if (activeConnection.getHeaderField("Error") != null)
+							retMsg += "\n" + activeConnection.getHeaderField("Error");
+
+						// Report our error
+						ByteArrayOutputStream o = new ByteArrayOutputStream();
+						System.out.println(new String(o.toByteArray(), "UTF-8").toString());
+						throw new RuntimeException(retCode+" "+retMsg);
+					}
+					*/
+				} catch (UnknownHostException ex) {
+					throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);
+				} catch (Exception ex) {
+					//if (cancel)
+					//	return; // assume cancel
+					if (ex instanceof RuntimeException)
+						throw (RuntimeException)ex;
+					throw new RuntimeException(ex.getMessage(), ex);
+				}	
+			}
 		}
 	}
@@ -278,4 +399,5 @@
 				line,
 				new JMenuItem(new ConvertToDataLayerAction()),
+				//new JMenuItem(new UploadTraceAction()),
 				new JSeparator(),
 				new JMenuItem(new RenameLayerAction(associatedFile, this)),
@@ -291,4 +413,5 @@
 				tagimage,
 				new JMenuItem(new ConvertToDataLayerAction()),
+				//new JMenuItem(new UploadTraceAction()),
 				new JSeparator(),
 				new JMenuItem(new RenameLayerAction(associatedFile, this)),
Index: /trunk/src/org/openstreetmap/josm/io/MultiPartFormOutputStream.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/MultiPartFormOutputStream.java	(revision 402)
+++ /trunk/src/org/openstreetmap/josm/io/MultiPartFormOutputStream.java	(revision 402)
@@ -0,0 +1,437 @@
+/*
+Taken from forum.java.sun.com
+
+License
+				
+Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved.
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+ 
+    * Redistribution of source code must retain the above copyright notice, this list 
+      of conditions and the following disclaimer.
+
+    * Redistribution in binary form must reproduce the above copyright notice, this 
+      list of conditions and the following disclaimer in the documentation and/or other 
+      materials provided with the distribution.
+
+ 
+Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to 
+endorse or promote products derived from this software without specific prior written 
+permission.
+ 
+This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED 
+CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, 
+INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS 
+A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT 
+WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, 
+INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 
+REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS 
+SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ 
+You acknowledge that this software is not designed, licensed or intended for use in the 
+design, construction, operation or maintenance of any nuclear facility. 
+*/
+
+package org.openstreetmap.josm.io;
+
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+
+	 
+/**
+ * <code>MultiPartFormOutputStream</code> is used to write 
+ * "multipart/form-data" to a <code>java.net.URLConnection</code> for 
+ * POSTing.  This is primarily for file uploading to HTTP servers.  
+ * 
+ * @since  JDK1.3
+ */
+public class MultiPartFormOutputStream extends OsmConnection {
+	/**
+	 * The line end characters.  
+	 */
+	private static final String NEWLINE = "\r\n";
+
+	/**
+	 * The boundary prefix.  
+	 */
+	private static final String PREFIX = "--";
+
+	/**
+	 * The output stream to write to.  
+	 */
+	private DataOutputStream out = null;
+
+	/**
+	 * The multipart boundary string.  
+	 */
+	private String boundary = null;
+
+	/**
+	 * Creates a new <code>MultiPartFormOutputStream</code> object using 
+	 * the specified output stream and boundary.  The boundary is required 
+	 * to be created before using this method, as described in the 
+	 * description for the <code>getContentType(String)</code> method.  
+	 * The boundary is only checked for <code>null</code> or empty string, 
+	 * but it is recommended to be at least 6 characters.  (Or use the 
+	 * static createBoundary() method to create one.)
+	 * 
+	 * @param  os        the output stream
+	 * @param  boundary  the boundary
+	 * @see  #createBoundary()
+	 * @see  #getContentType(String)
+	 */
+	public MultiPartFormOutputStream(OutputStream os, String boundary) {
+		if(os == null) {
+			throw new IllegalArgumentException("Output stream is required.");
+		}
+		if(boundary == null || boundary.length() == 0) {
+			throw new IllegalArgumentException("Boundary stream is required.");
+		}
+		this.out = new DataOutputStream(os);
+		this.boundary = boundary;
+		initAuthentication();
+	}
+
+	/**
+	 * Writes an boolean field value.  
+	 * 
+	 * @param  name   the field name (required)
+	 * @param  value  the field value
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeField(String name, boolean value) 
+	throws java.io.IOException {
+		writeField(name, new Boolean(value).toString());
+	}
+
+	/**
+	 * Writes an double field value.  
+	 * 
+	 * @param  name   the field name (required)
+	 * @param  value  the field value
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeField(String name, double value) 
+	throws java.io.IOException {
+		writeField(name, Double.toString(value));
+	}
+
+	/**
+	 * Writes an float field value.  
+	 * 
+	 * @param  name   the field name (required)
+	 * @param  value  the field value
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeField(String name, float value) 
+	throws java.io.IOException {
+		writeField(name, Float.toString(value));
+	}
+
+	/**
+	 * Writes an long field value.  
+	 * 
+	 * @param  name   the field name (required)
+	 * @param  value  the field value
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeField(String name, long value) 
+	throws java.io.IOException {
+		writeField(name, Long.toString(value));
+	}
+
+	/**
+	 * Writes an int field value.  
+	 * 
+	 * @param  name   the field name (required)
+	 * @param  value  the field value
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeField(String name, int value) 
+	throws java.io.IOException {
+		writeField(name, Integer.toString(value));
+	}
+
+	/**
+	 * Writes an short field value.  
+	 * 
+	 * @param  name   the field name (required)
+	 * @param  value  the field value
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeField(String name, short value) 
+	throws java.io.IOException {
+		writeField(name, Short.toString(value));
+	}
+
+	/**
+	 * Writes an char field value.  
+	 * 
+	 * @param  name   the field name (required)
+	 * @param  value  the field value
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeField(String name, char value) 
+	throws java.io.IOException {
+		writeField(name, new Character(value).toString());
+	}
+
+	/**
+	 * Writes an string field value.  If the value is null, an empty string 
+	 * is sent ("").  
+	 * 
+	 * @param  name   the field name (required)
+	 * @param  value  the field value
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeField(String name, String value) 
+	throws java.io.IOException {
+		if(name == null) {
+			throw new IllegalArgumentException("Name cannot be null or empty.");
+		}
+		if(value == null) {
+			value = "";
+		}
+		/*
+			--boundary\r\n
+			Content-Disposition: form-data; name="<fieldName>"\r\n
+			\r\n
+			<value>\r\n
+		 */
+		// write boundary
+		out.writeBytes(PREFIX);
+		out.writeBytes(boundary);
+		out.writeBytes(NEWLINE);
+		// write content header
+		out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"");
+		out.writeBytes(NEWLINE);
+		out.writeBytes(NEWLINE);
+		// write content
+		out.writeBytes(value);
+		out.writeBytes(NEWLINE);
+		out.flush();
+	}
+
+	/**
+	 * Writes a file's contents.  If the file is null, does not exists, or 
+	 * is a directory, a <code>java.lang.IllegalArgumentException</code> 
+	 * will be thrown.  
+	 * 
+	 * @param  name      the field name
+	 * @param  mimeType  the file content type (optional, recommended)
+	 * @param  file      the file (the file must exist)
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeFile(String name, String mimeType, java.io.File file) 
+	throws java.io.IOException {
+		if(file == null) {
+			throw new IllegalArgumentException("File cannot be null.");
+		}
+		if(!file.exists()) {
+			throw new IllegalArgumentException("File does not exist.");
+		}
+		if(file.isDirectory()) {
+			throw new IllegalArgumentException("File cannot be a directory.");
+		}
+		writeFile(name, mimeType, file.getCanonicalPath(), new FileInputStream(file));
+	}
+
+	/**
+	 * Writes a input stream's contents.  If the input stream is null, a 
+	 * <code>java.lang.IllegalArgumentException</code> will be thrown.  
+	 * 
+	 * @param  name      the field name
+	 * @param  mimeType  the file content type (optional, recommended)
+	 * @param  fileName  the file name (required)
+	 * @param  is        the input stream
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeFile(String name, String mimeType, 
+			String fileName, InputStream is) 
+	throws java.io.IOException {
+		if(is == null) {
+			throw new IllegalArgumentException("Input stream cannot be null.");
+		}
+		if(fileName == null || fileName.length() == 0) {
+			throw new IllegalArgumentException("File name cannot be null or empty.");
+		}
+		/*
+			--boundary\r\n
+			Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
+			Content-Type: <mime-type>\r\n
+			\r\n
+			<file-data>\r\n
+		 */
+		// write boundary
+		out.writeBytes(PREFIX);
+		out.writeBytes(boundary);
+		out.writeBytes(NEWLINE);
+		// write content header
+		out.writeBytes("Content-Disposition: form-data; name=\"" + name + 
+				"\"; filename=\"" + fileName + "\"");
+		out.writeBytes(NEWLINE);
+		if(mimeType != null) {
+			out.writeBytes("Content-Type: " + mimeType);
+			out.writeBytes(NEWLINE);
+		}
+		out.writeBytes(NEWLINE);
+		// write content
+		byte[] data = new byte[1024];
+		int r = 0;
+		while((r = is.read(data, 0, data.length)) != -1) {
+			out.write(data, 0, r);
+		}
+		// close input stream, but ignore any possible exception for it
+		try {
+			is.close();
+		} catch(Exception e) {}
+		out.writeBytes(NEWLINE);
+		out.flush();
+	}
+
+	/**
+	 * Writes the given bytes.  The bytes are assumed to be the contents 
+	 * of a file, and will be sent as such.  If the data is null, a 
+	 * <code>java.lang.IllegalArgumentException</code> will be thrown.  
+	 * 
+	 * @param  name      the field name
+	 * @param  mimeType  the file content type (optional, recommended)
+	 * @param  fileName  the file name (required)
+	 * @param  data      the file data
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void writeFile(String name, String mimeType, 
+			String fileName, byte[] data) 
+	throws java.io.IOException {
+		if(data == null) {
+			throw new IllegalArgumentException("Data cannot be null.");
+		}
+		if(fileName == null || fileName.length() == 0) {
+			throw new IllegalArgumentException("File name cannot be null or empty.");
+		}
+		/*
+			--boundary\r\n
+			Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
+			Content-Type: <mime-type>\r\n
+			\r\n
+			<file-data>\r\n
+		 */
+		// write boundary
+		out.writeBytes(PREFIX);
+		out.writeBytes(boundary);
+		out.writeBytes(NEWLINE);
+		// write content header
+		out.writeBytes("Content-Disposition: form-data; name=\"" + name + 
+				"\"; filename=\"" + fileName + "\"");
+		out.writeBytes(NEWLINE);
+		if(mimeType != null) {
+			out.writeBytes("Content-Type: " + mimeType);
+			out.writeBytes(NEWLINE);
+		}
+		out.writeBytes(NEWLINE);
+		// write content
+		out.write(data, 0, data.length);
+		out.writeBytes(NEWLINE);
+		out.flush();
+	}
+
+	/**
+	 * Flushes the stream.  Actually, this method does nothing, as the only 
+	 * write methods are highly specialized and automatically flush.  
+	 * 
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void flush() throws java.io.IOException {
+		// out.flush();
+	}
+
+	/**
+	 * Closes the stream.  <br/>
+	 * <br/>
+	 * <b>NOTE:</b> This method <b>MUST</b> be called to finalize the 
+	 * multipart stream.
+	 * 
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public void close() throws java.io.IOException {
+		// write final boundary
+		out.writeBytes(PREFIX);
+		out.writeBytes(boundary);
+		out.writeBytes(PREFIX);
+		out.writeBytes(NEWLINE);
+		out.flush();
+		out.close();
+	}
+
+	/**
+	 * Gets the multipart boundary string being used by this stream.  
+	 * 
+	 * @return  the boundary
+	 */
+	public String getBoundary() {
+		return this.boundary;
+	}
+
+	/**
+	 * Creates a new <code>java.net.URLConnection</code> object from the 
+	 * specified <code>java.net.URL</code>.  This is a convenience method 
+	 * which will set the <code>doInput</code>, <code>doOutput</code>, 
+	 * <code>useCaches</code> and <code>defaultUseCaches</code> fields to 
+	 * the appropriate settings in the correct order.  
+	 * 
+	 * @return  a <code>java.net.URLConnection</code> object for the URL
+	 * @throws  java.io.IOException  on input/output errors
+	 */
+	public static URLConnection createConnection(URL url) 
+	throws java.io.IOException {
+		URLConnection urlConn = url.openConnection();
+		if(urlConn instanceof HttpURLConnection) {
+			HttpURLConnection httpConn = (HttpURLConnection)urlConn;
+			httpConn.setRequestMethod("POST");
+		}
+		urlConn.setDoInput(true);
+		urlConn.setDoOutput(true);
+		urlConn.setUseCaches(false);
+		urlConn.setDefaultUseCaches(false);
+		return urlConn;
+	}
+
+	/**
+	 * Creates a multipart boundary string by concatenating 20 hyphens (-) 
+	 * and the hexadecimal (base-16) representation of the current time in 
+	 * milliseconds.  
+	 * 
+	 * @return  a multipart boundary string
+	 * @see  #getContentType(String)
+	 */
+	public static String createBoundary() {
+		return "--------------------" + 
+		Long.toString(System.currentTimeMillis(), 16);
+	}
+
+	/**
+	 * Gets the content type string suitable for the 
+	 * <code>java.net.URLConnection</code> which includes the multipart 
+	 * boundary string.  <br/>
+	 * <br/>
+	 * This method is static because, due to the nature of the 
+	 * <code>java.net.URLConnection</code> class, once the output stream 
+	 * for the connection is acquired, it's too late to set the content 
+	 * type (or any other request parameter).  So one has to create a 
+	 * multipart boundary string first before using this class, such as 
+	 * with the <code>createBoundary()</code> method.  
+	 * 
+	 * @param  boundary  the boundary string
+	 * @return  the content type string
+	 * @see  #createBoundary()
+	 */
+	public static String getContentType(String boundary) {
+		return "multipart/form-data; boundary=" + boundary;
+	}
+}
