| Constructor: |
public MimeMultipart() {
this("mixed");
}
|
public MimeMultipart(String subtype) {
super();
/*
* Compute a boundary string.
*/
String boundary = UniqueValue.getUniqueBoundaryValue();
ContentType cType = new ContentType("multipart", subtype, null);
cType.setParameter("boundary", boundary);
contentType = cType.toString();
}
|
public MimeMultipart(DataSource ds) throws MessagingException {
super();
if (ds instanceof MessageAware) {
MessageContext mc = ((MessageAware)ds).getMessageContext();
setParent(mc.getPart());
}
if (ds instanceof MultipartDataSource) {
// ask super to do this for us.
setMultipartDataSource((MultipartDataSource)ds);
return;
}
// 'ds' was not a MultipartDataSource, we have
// to parse this ourself.
parsed = false;
this.ds = ds;
contentType = ds.getContentType();
}
Constructs a MimeMultipart object and its bodyparts from the
given DataSource.
This constructor handles as a special case the situation where the
given DataSource is a MultipartDataSource object. In this case, this
method just invokes the superclass (i.e., Multipart) constructor
that takes a MultipartDataSource object.
Otherwise, the DataSource is assumed to provide a MIME multipart
byte stream. The parsed flag is set to false. When
the data for the body parts are needed, the parser extracts the
"boundary" parameter from the content type of this DataSource,
skips the 'preamble' and reads bytes till the terminating
boundary and creates MimeBodyParts for each part of the stream. Parameters:
ds - DataSource, can be a MultipartDataSource
|
| Method from javax.mail.internet.MimeMultipart Detail: |
public synchronized void addBodyPart(BodyPart part) throws MessagingException {
parse();
super.addBodyPart(part);
}
Adds a Part to the multipart. The BodyPart is appended to
the list of existing Parts. |
public synchronized void addBodyPart(BodyPart part,
int index) throws MessagingException {
parse();
super.addBodyPart(part, index);
}
Adds a BodyPart at position index.
If index is not the last one in the list,
the subsequent parts are shifted up. If index
is larger than the number of parts present, the
BodyPart is appended to the end. |
protected InternetHeaders createInternetHeaders(InputStream is) throws MessagingException {
return new InternetHeaders(is);
}
Create and return an InternetHeaders object that loads the
headers from the given InputStream. Subclasses can override
this method to return a subclass of InternetHeaders, if
necessary. This implementation simply constructs and returns
an InternetHeaders object. |
protected MimeBodyPart createMimeBodyPart(InputStream is) throws MessagingException {
return new MimeBodyPart(is);
}
Create and return a MimeBodyPart object to represent a
body part parsed from the InputStream. Subclasses can override
this method to return a subclass of MimeBodyPart, if
necessary. This implementation simply constructs and returns
a MimeBodyPart object. |
protected MimeBodyPart createMimeBodyPart(InternetHeaders headers,
byte[] content) throws MessagingException {
return new MimeBodyPart(headers, content);
}
Create and return a MimeBodyPart object to represent a
body part parsed from the InputStream. Subclasses can override
this method to return a subclass of MimeBodyPart, if
necessary. This implementation simply constructs and returns
a MimeBodyPart object. |
public synchronized BodyPart getBodyPart(int index) throws MessagingException {
parse();
return super.getBodyPart(index);
}
Get the specified BodyPart. BodyParts are numbered starting at 0. |
public synchronized BodyPart getBodyPart(String CID) throws MessagingException {
parse();
int count = getCount();
for (int i = 0; i < count; i++) {
MimeBodyPart part = (MimeBodyPart)getBodyPart(i);
String s = part.getContentID();
if (s != null && s.equals(CID))
return part;
}
return null;
}
Get the MimeBodyPart referred to by the given ContentID (CID).
Returns null if the part is not found. |
public synchronized int getCount() throws MessagingException {
parse();
return super.getCount();
}
Return the number of enclosed BodyPart objects. |
public synchronized String getPreamble() throws MessagingException {
parse();
return preamble;
}
Get the preamble text, if any, that appears before the
first body part of this multipart. Some protocols,
such as IMAP, will not allow access to the preamble text. |
public synchronized boolean isComplete() throws MessagingException {
parse();
return complete;
}
Return true if the final boundary line for this
multipart was seen. When parsing multipart content,
this class will (by default) terminate parsing with
no error if the end of input is reached before seeing
the final multipart boundary line. In such a case,
this method will return false. (If the System property
"mail.mime.multipart.ignoremissingendboundary" is set to
false, parsing such a message will instead throw a
MessagingException.) |
protected synchronized void parse() throws MessagingException {
if (parsed)
return;
if (bmparse) {
parsebm();
return;
}
InputStream in = null;
SharedInputStream sin = null;
long start = 0, end = 0;
try {
in = ds.getInputStream();
if (!(in instanceof ByteArrayInputStream) &&
!(in instanceof BufferedInputStream) &&
!(in instanceof SharedInputStream))
in = new BufferedInputStream(in);
} catch (Exception ex) {
throw new MessagingException("No inputstream from datasource", ex);
}
if (in instanceof SharedInputStream)
sin = (SharedInputStream)in;
ContentType cType = new ContentType(contentType);
String boundary = null;
String bp = cType.getParameter("boundary");
if (bp != null)
boundary = "--" + bp;
else if (!ignoreMissingBoundaryParameter)
throw new MessagingException("Missing boundary parameter");
try {
// Skip and save the preamble
LineInputStream lin = new LineInputStream(in);
StringBuffer preamblesb = null;
String line;
String lineSeparator = null;
while ((line = lin.readLine()) != null) {
/*
* Strip trailing whitespace. Can't use trim method
* because it's too aggressive. Some bogus MIME
* messages will include control characters in the
* boundary string.
*/
int i;
for (i = line.length() - 1; i >= 0; i--) {
char c = line.charAt(i);
if (!(c == ' " || c == '\t"))
break;
}
line = line.substring(0, i + 1);
if (boundary != null) {
if (line.equals(boundary))
break;
} else {
/*
* Boundary hasn't been defined, does this line
* look like a boundary? If so, assume it is
* the boundary and save it.
*/
if (line.startsWith("--")) {
boundary = line;
break;
}
}
// save the preamble after skipping blank lines
if (line.length() > 0) {
// if we haven't figured out what the line seprator
// is, do it now
if (lineSeparator == null) {
try {
lineSeparator =
System.getProperty("line.separator", "\n");
} catch (SecurityException ex) {
lineSeparator = "\n";
}
}
// accumulate the preamble
if (preamblesb == null)
preamblesb = new StringBuffer(line.length() + 2);
preamblesb.append(line).append(lineSeparator);
}
}
if (line == null)
throw new MessagingException("Missing start boundary");
if (preamblesb != null)
preamble = preamblesb.toString();
// save individual boundary bytes for easy comparison later
byte[] bndbytes = ASCIIUtility.getBytes(boundary);
int bl = bndbytes.length;
/*
* Read and process body parts until we see the
* terminating boundary line (or EOF).
*/
boolean done = false;
getparts:
while (!done) {
InternetHeaders headers = null;
if (sin != null) {
start = sin.getPosition();
// skip headers
while ((line = lin.readLine()) != null && line.length() > 0)
;
if (line == null) {
if (!ignoreMissingEndBoundary)
throw new MessagingException(
"missing multipart end boundary");
// assume there's just a missing end boundary
complete = false;
break getparts;
}
} else {
// collect the headers for this body part
headers = createInternetHeaders(in);
}
if (!in.markSupported())
throw new MessagingException("Stream doesn't support mark");
ByteArrayOutputStream buf = null;
// if we don't have a shared input stream, we copy the data
if (sin == null)
buf = new ByteArrayOutputStream();
else
end = sin.getPosition();
int b;
boolean bol = true; // beginning of line flag
// the two possible end of line characters
int eol1 = -1, eol2 = -1;
/*
* Read and save the content bytes in buf.
*/
for (;;) {
if (bol) {
/*
* At the beginning of a line, check whether the
* next line is a boundary.
*/
int i;
in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP
// read bytes, matching against the boundary
for (i = 0; i < bl; i++)
if (in.read() != (bndbytes[i] & 0xff))
break;
if (i == bl) {
// matched the boundary, check for last boundary
int b2 = in.read();
if (b2 == '-") {
if (in.read() == '-") {
complete = true;
done = true;
break; // ignore trailing text
}
}
// skip linear whitespace
while (b2 == ' " || b2 == '\t")
b2 = in.read();
// check for end of line
if (b2 == '\n")
break; // got it! break out of the loop
if (b2 == '\r") {
in.mark(1);
if (in.read() != '\n")
in.reset();
break; // got it! break out of the loop
}
}
// failed to match, reset and proceed normally
in.reset();
// if this is not the first line, write out the
// end of line characters from the previous line
if (buf != null && eol1 != -1) {
buf.write(eol1);
if (eol2 != -1)
buf.write(eol2);
eol1 = eol2 = -1;
}
}
// read the next byte
if ((b = in.read()) < 0) {
if (!ignoreMissingEndBoundary)
throw new MessagingException(
"missing multipart end boundary");
complete = false;
done = true;
break;
}
/*
* If we're at the end of the line, save the eol characters
* to be written out before the beginning of the next line.
*/
if (b == '\r" || b == '\n") {
bol = true;
if (sin != null)
end = sin.getPosition() - 1;
eol1 = b;
if (b == '\r") {
in.mark(1);
if ((b = in.read()) == '\n")
eol2 = b;
else
in.reset();
}
} else {
bol = false;
if (buf != null)
buf.write(b);
}
}
/*
* Create a MimeBody element to represent this body part.
*/
MimeBodyPart part;
if (sin != null)
part = createMimeBodyPart(sin.newStream(start, end));
else
part = createMimeBodyPart(headers, buf.toByteArray());
super.addBodyPart(part);
}
} catch (IOException ioex) {
throw new MessagingException("IO Error", ioex);
} finally {
try {
in.close();
} catch (IOException cex) {
// ignore
}
}
parsed = true;
}
Parse the InputStream from our DataSource, constructing the
appropriate MimeBodyParts. The parsed flag is
set to true, and if true on entry nothing is done. This
method is called by all other methods that need data for
the body parts, to make sure the data has been parsed. |
public boolean removeBodyPart(BodyPart part) throws MessagingException {
parse();
return super.removeBodyPart(part);
}
Remove the specified part from the multipart message.
Shifts all the parts after the removed part down one. |
public void removeBodyPart(int index) throws MessagingException {
parse();
super.removeBodyPart(index);
}
Remove the part at specified location (starting from 0).
Shifts all the parts after the removed part down one. |
public synchronized void setPreamble(String preamble) throws MessagingException {
this.preamble = preamble;
}
Set the preamble text to be included before the first
body part. Applications should generally not include
any preamble text. In some cases it may be helpful to
include preamble text with instructions for users of
pre-MIME software. The preamble text should be complete
lines, including newlines. |
public synchronized void setSubType(String subtype) throws MessagingException {
ContentType cType = new ContentType(contentType);
cType.setSubType(subtype);
contentType = cType.toString();
}
Set the subtype. This method should be invoked only on a new
MimeMultipart object created by the client. The default subtype
of such a multipart object is "mixed". |
protected void updateHeaders() throws MessagingException {
for (int i = 0; i < parts.size(); i++)
((MimeBodyPart)parts.elementAt(i)).updateHeaders();
}
Update headers. The default implementation here just
calls the updateHeaders method on each of its
children BodyParts.
Note that the boundary parameter is already set up when
a new and empty MimeMultipart object is created.
This method is called when the saveChanges
method is invoked on the Message object containing this
Multipart. This is typically done as part of the Message
send process, however note that a client is free to call
it any number of times. So if the header updating process is
expensive for a specific MimeMultipart subclass, then it
might itself want to track whether its internal state actually
did change, and do the header updating only if necessary. |
public synchronized void writeTo(OutputStream os) throws IOException, MessagingException {
parse();
String boundary = "--" +
(new ContentType(contentType)).getParameter("boundary");
LineOutputStream los = new LineOutputStream(os);
// if there's a preamble, write it out
if (preamble != null) {
byte[] pb = ASCIIUtility.getBytes(preamble);
los.write(pb);
// make sure it ends with a newline
if (pb.length > 0 &&
!(pb[pb.length-1] == '\r" || pb[pb.length-1] == '\n")) {
los.writeln();
}
// XXX - could force a blank line before start boundary
}
for (int i = 0; i < parts.size(); i++) {
los.writeln(boundary); // put out boundary
((MimeBodyPart)parts.elementAt(i)).writeTo(os);
los.writeln(); // put out empty line
}
// put out last boundary
los.writeln(boundary + "--");
}
Iterates through all the parts and outputs each MIME part
separated by a boundary. |