1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/packaging/mime/internet/MimeMultipart.java Wed Apr 27 01:27:09 2016 +0800 1.3 @@ -0,0 +1,653 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +/* 1.30 + * @(#)MimeMultipart.java 1.31 03/01/29 1.31 + */ 1.32 + 1.33 + 1.34 + 1.35 +package com.sun.xml.internal.messaging.saaj.packaging.mime.internet; 1.36 + 1.37 +import java.io.*; 1.38 + 1.39 +import javax.activation.DataSource; 1.40 + 1.41 +import com.sun.xml.internal.messaging.saaj.packaging.mime.*; 1.42 +import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*; 1.43 +import com.sun.xml.internal.messaging.saaj.util.FinalArrayList; 1.44 +import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream; 1.45 +import com.sun.xml.internal.messaging.saaj.util.SAAJUtil; 1.46 + 1.47 +/** 1.48 + * The MimeMultipart class is an implementation 1.49 + * that uses MIME conventions for the multipart data. <p> 1.50 + * 1.51 + * A MimeMultipart is obtained from a MimeBodyPart whose primary type 1.52 + * is "multipart" (by invoking the part's <code>getContent()</code> method) 1.53 + * or it can be created by a client as part of creating a new MimeMessage. <p> 1.54 + * 1.55 + * The default multipart subtype is "mixed". The other multipart 1.56 + * subtypes, such as "alternative", "related", and so on, can be 1.57 + * implemented as subclasses of MimeMultipart with additional methods 1.58 + * to implement the additional semantics of that type of multipart 1.59 + * content. The intent is that service providers, mail JavaBean writers 1.60 + * and mail clients will write many such subclasses and their Command 1.61 + * Beans, and will install them into the JavaBeans Activation 1.62 + * Framework, so that any JavaMail implementation and its clients can 1.63 + * transparently find and use these classes. Thus, a MIME multipart 1.64 + * handler is treated just like any other type handler, thereby 1.65 + * decoupling the process of providing multipart handlers from the 1.66 + * JavaMail API. Lacking these additional MimeMultipart subclasses, 1.67 + * all subtypes of MIME multipart data appear as MimeMultipart objects. <p> 1.68 + * 1.69 + * An application can directly construct a MIME multipart object of any 1.70 + * subtype by using the <code>MimeMultipart(String subtype)</code> 1.71 + * constructor. For example, to create a "multipart/alternative" object, 1.72 + * use <code>new MimeMultipart("alternative")</code>. 1.73 + * 1.74 + * @version 1.31, 03/01/29 1.75 + * @author John Mani 1.76 + * @author Bill Shannon 1.77 + * @author Max Spivak 1.78 + */ 1.79 + 1.80 +//BM MimeMultipart can extend this 1.81 +public class MimeMultipart { 1.82 + 1.83 + /** 1.84 + * The DataSource supplying our InputStream. 1.85 + */ 1.86 + protected DataSource ds = null; 1.87 + 1.88 + /** 1.89 + * Have we parsed the data from our InputStream yet? 1.90 + * Defaults to true; set to false when our constructor is 1.91 + * given a DataSource with an InputStream that we need to 1.92 + * parse. 1.93 + */ 1.94 + protected boolean parsed = true; 1.95 + 1.96 + /** 1.97 + * Vector of MimeBodyPart objects. 1.98 + */ 1.99 + protected FinalArrayList parts = new FinalArrayList(); // Holds BodyParts 1.100 + 1.101 + /** 1.102 + * This field specifies the content-type of this multipart 1.103 + * object. It defaults to "multipart/mixed". 1.104 + */ 1.105 + protected ContentType contentType; 1.106 + 1.107 + /** 1.108 + * The <code>MimeBodyPart</code> containing this <code>MimeMultipart</code>, 1.109 + * if known. 1.110 + * @since JavaMail 1.1 1.111 + */ 1.112 + protected MimeBodyPart parent; 1.113 + 1.114 + protected static final boolean ignoreMissingEndBoundary; 1.115 + static { 1.116 + ignoreMissingEndBoundary = SAAJUtil.getSystemBoolean("saaj.mime.multipart.ignoremissingendboundary"); 1.117 + } 1.118 + 1.119 + /** 1.120 + * Default constructor. An empty MimeMultipart object 1.121 + * is created. Its content type is set to "multipart/mixed". 1.122 + * A unique boundary string is generated and this string is 1.123 + * setup as the "boundary" parameter for the 1.124 + * <code>contentType</code> field. <p> 1.125 + * 1.126 + * MimeBodyParts may be added later. 1.127 + */ 1.128 + public MimeMultipart() { 1.129 + this("mixed"); 1.130 + } 1.131 + 1.132 + /** 1.133 + * Construct a MimeMultipart object of the given subtype. 1.134 + * A unique boundary string is generated and this string is 1.135 + * setup as the "boundary" parameter for the 1.136 + * <code>contentType</code> field. <p> 1.137 + * 1.138 + * MimeBodyParts may be added later. 1.139 + */ 1.140 + public MimeMultipart(String subtype) { 1.141 + //super(); 1.142 + /* 1.143 + * Compute a boundary string. 1.144 + */ 1.145 + String boundary = UniqueValue.getUniqueBoundaryValue(); 1.146 + contentType = new ContentType("multipart", subtype, null); 1.147 + contentType.setParameter("boundary", boundary); 1.148 + } 1.149 + 1.150 + /** 1.151 + * Constructs a MimeMultipart object and its bodyparts from the 1.152 + * given DataSource. <p> 1.153 + * 1.154 + * This constructor handles as a special case the situation where the 1.155 + * given DataSource is a MultipartDataSource object. 1.156 + * 1.157 + * Otherwise, the DataSource is assumed to provide a MIME multipart 1.158 + * byte stream. The <code>parsed</code> flag is set to false. When 1.159 + * the data for the body parts are needed, the parser extracts the 1.160 + * "boundary" parameter from the content type of this DataSource, 1.161 + * skips the 'preamble' and reads bytes till the terminating 1.162 + * boundary and creates MimeBodyParts for each part of the stream. 1.163 + * 1.164 + * @param ds DataSource, can be a MultipartDataSource 1.165 + * @param ct 1.166 + * This must be the same information as {@link DataSource#getContentType()}. 1.167 + * All the callers of this method seem to have this object handy, so 1.168 + * for performance reason this method accepts it. Can be null. 1.169 + */ 1.170 + public MimeMultipart(DataSource ds, ContentType ct) throws MessagingException { 1.171 + // 'ds' was not a MultipartDataSource, we have 1.172 + // to parse this ourself. 1.173 + parsed = false; 1.174 + this.ds = ds; 1.175 + if (ct==null) 1.176 + contentType = new ContentType(ds.getContentType()); 1.177 + else 1.178 + contentType = ct; 1.179 + } 1.180 + 1.181 + /** 1.182 + * Set the subtype. This method should be invoked only on a new 1.183 + * MimeMultipart object created by the client. The default subtype 1.184 + * of such a multipart object is "mixed". <p> 1.185 + * 1.186 + * @param subtype Subtype 1.187 + */ 1.188 + public void setSubType(String subtype) { 1.189 + contentType.setSubType(subtype); 1.190 + } 1.191 + 1.192 + /** 1.193 + * Return the number of enclosed MimeBodyPart objects. 1.194 + * 1.195 + * @return number of parts 1.196 + */ 1.197 + public int getCount() throws MessagingException { 1.198 + parse(); 1.199 + if (parts == null) 1.200 + return 0; 1.201 + 1.202 + return parts.size(); 1.203 + } 1.204 + 1.205 + /** 1.206 + * Get the specified MimeBodyPart. BodyParts are numbered starting at 0. 1.207 + * 1.208 + * @param index the index of the desired MimeBodyPart 1.209 + * @return the MimeBodyPart 1.210 + * @exception MessagingException if no such MimeBodyPart exists 1.211 + */ 1.212 + public MimeBodyPart getBodyPart(int index) 1.213 + throws MessagingException { 1.214 + parse(); 1.215 + if (parts == null) 1.216 + throw new IndexOutOfBoundsException("No such BodyPart"); 1.217 + 1.218 + return (MimeBodyPart)parts.get(index); 1.219 + } 1.220 + 1.221 + /** 1.222 + * Get the MimeBodyPart referred to by the given ContentID (CID). 1.223 + * Returns null if the part is not found. 1.224 + * 1.225 + * @param CID the ContentID of the desired part 1.226 + * @return the MimeBodyPart 1.227 + */ 1.228 + public MimeBodyPart getBodyPart(String CID) 1.229 + throws MessagingException { 1.230 + parse(); 1.231 + 1.232 + int count = getCount(); 1.233 + for (int i = 0; i < count; i++) { 1.234 + MimeBodyPart part = getBodyPart(i); 1.235 + String s = part.getContentID(); 1.236 + // Old versions of AXIS2 put angle brackets around the content 1.237 + // id but not the start param 1.238 + String sNoAngle = (s!= null) ? s.replaceFirst("^<", "").replaceFirst(">$", "") 1.239 + :null; 1.240 + if (s != null && (s.equals(CID) || CID.equals(sNoAngle))) 1.241 + return part; 1.242 + } 1.243 + return null; 1.244 + } 1.245 + 1.246 + /** 1.247 + * Update headers. The default implementation here just 1.248 + * calls the <code>updateHeaders</code> method on each of its 1.249 + * children BodyParts. <p> 1.250 + * 1.251 + * Note that the boundary parameter is already set up when 1.252 + * a new and empty MimeMultipart object is created. <p> 1.253 + * 1.254 + * This method is called when the <code>saveChanges</code> 1.255 + * method is invoked on the Message object containing this 1.256 + * MimeMultipart. This is typically done as part of the Message 1.257 + * send process, however note that a client is free to call 1.258 + * it any number of times. So if the header updating process is 1.259 + * expensive for a specific MimeMultipart subclass, then it 1.260 + * might itself want to track whether its internal state actually 1.261 + * did change, and do the header updating only if necessary. 1.262 + */ 1.263 + protected void updateHeaders() throws MessagingException { 1.264 + for (int i = 0; i < parts.size(); i++) 1.265 + ((MimeBodyPart)parts.get(i)).updateHeaders(); 1.266 + } 1.267 + 1.268 + /** 1.269 + * Iterates through all the parts and outputs each Mime part 1.270 + * separated by a boundary. 1.271 + */ 1.272 + public void writeTo(OutputStream os) 1.273 + throws IOException, MessagingException { 1.274 + parse(); 1.275 + 1.276 + String boundary = "--" + contentType.getParameter("boundary"); 1.277 + 1.278 + for (int i = 0; i < parts.size(); i++) { 1.279 + OutputUtil.writeln(boundary, os); // put out boundary 1.280 + getBodyPart(i).writeTo(os); 1.281 + OutputUtil.writeln(os); // put out empty line 1.282 + } 1.283 + 1.284 + // put out last boundary 1.285 + OutputUtil.writeAsAscii(boundary, os); 1.286 + OutputUtil.writeAsAscii("--", os); 1.287 + os.flush(); 1.288 + } 1.289 + 1.290 + /** 1.291 + * Parse the InputStream from our DataSource, constructing the 1.292 + * appropriate MimeBodyParts. The <code>parsed</code> flag is 1.293 + * set to true, and if true on entry nothing is done. This 1.294 + * method is called by all other methods that need data for 1.295 + * the body parts, to make sure the data has been parsed. 1.296 + * 1.297 + * @since JavaMail 1.2 1.298 + */ 1.299 + protected void parse() throws MessagingException { 1.300 + if (parsed) 1.301 + return; 1.302 + 1.303 + InputStream in; 1.304 + SharedInputStream sin = null; 1.305 + long start = 0, end = 0; 1.306 + boolean foundClosingBoundary = false; 1.307 + 1.308 + try { 1.309 + in = ds.getInputStream(); 1.310 + if (!(in instanceof ByteArrayInputStream) && 1.311 + !(in instanceof BufferedInputStream) && 1.312 + !(in instanceof SharedInputStream)) 1.313 + in = new BufferedInputStream(in); 1.314 + } catch (Exception ex) { 1.315 + throw new MessagingException("No inputstream from datasource"); 1.316 + } 1.317 + if (in instanceof SharedInputStream) 1.318 + sin = (SharedInputStream)in; 1.319 + 1.320 + String boundary = "--" + contentType.getParameter("boundary"); 1.321 + byte[] bndbytes = ASCIIUtility.getBytes(boundary); 1.322 + int bl = bndbytes.length; 1.323 + 1.324 + try { 1.325 + // Skip the preamble 1.326 + LineInputStream lin = new LineInputStream(in); 1.327 + String line; 1.328 + while ((line = lin.readLine()) != null) { 1.329 + /* 1.330 + * Strip trailing whitespace. Can't use trim method 1.331 + * because it's too aggressive. Some bogus MIME 1.332 + * messages will include control characters in the 1.333 + * boundary string. 1.334 + */ 1.335 + int i; 1.336 + for (i = line.length() - 1; i >= 0; i--) { 1.337 + char c = line.charAt(i); 1.338 + if (!(c == ' ' || c == '\t')) 1.339 + break; 1.340 + } 1.341 + line = line.substring(0, i + 1); 1.342 + if (line.equals(boundary)) 1.343 + break; 1.344 + } 1.345 + if (line == null) 1.346 + throw new MessagingException("Missing start boundary"); 1.347 + 1.348 + /* 1.349 + * Read and process body parts until we see the 1.350 + * terminating boundary line (or EOF). 1.351 + */ 1.352 + boolean done = false; 1.353 + getparts: 1.354 + while (!done) { 1.355 + InternetHeaders headers = null; 1.356 + if (sin != null) { 1.357 + start = sin.getPosition(); 1.358 + // skip headers 1.359 + while ((line = lin.readLine()) != null && line.length() > 0) 1.360 + ; 1.361 + if (line == null) { 1.362 + if (!ignoreMissingEndBoundary) { 1.363 + throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers"); 1.364 + } 1.365 + // assume there's just a missing end boundary 1.366 + break getparts; 1.367 + } 1.368 + } else { 1.369 + // collect the headers for this body part 1.370 + headers = createInternetHeaders(in); 1.371 + } 1.372 + 1.373 + if (!in.markSupported()) 1.374 + throw new MessagingException("Stream doesn't support mark"); 1.375 + 1.376 + ByteOutputStream buf = null; 1.377 + // if we don't have a shared input stream, we copy the data 1.378 + if (sin == null) 1.379 + buf = new ByteOutputStream(); 1.380 + int b; 1.381 + boolean bol = true; // beginning of line flag 1.382 + // the two possible end of line characters 1.383 + int eol1 = -1, eol2 = -1; 1.384 + 1.385 + /* 1.386 + * Read and save the content bytes in buf. 1.387 + */ 1.388 + for (;;) { 1.389 + if (bol) { 1.390 + /* 1.391 + * At the beginning of a line, check whether the 1.392 + * next line is a boundary. 1.393 + */ 1.394 + int i; 1.395 + in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP 1.396 + // read bytes, matching against the boundary 1.397 + for (i = 0; i < bl; i++) 1.398 + if (in.read() != bndbytes[i]) 1.399 + break; 1.400 + if (i == bl) { 1.401 + // matched the boundary, check for last boundary 1.402 + int b2 = in.read(); 1.403 + if (b2 == '-') { 1.404 + if (in.read() == '-') { 1.405 + done = true; 1.406 + foundClosingBoundary = true; 1.407 + break; // ignore trailing text 1.408 + } 1.409 + } 1.410 + // skip linear whitespace 1.411 + while (b2 == ' ' || b2 == '\t') 1.412 + b2 = in.read(); 1.413 + // check for end of line 1.414 + if (b2 == '\n') 1.415 + break; // got it! break out of the loop 1.416 + if (b2 == '\r') { 1.417 + in.mark(1); 1.418 + if (in.read() != '\n') 1.419 + in.reset(); 1.420 + break; // got it! break out of the loop 1.421 + } 1.422 + } 1.423 + // failed to match, reset and proceed normally 1.424 + in.reset(); 1.425 + 1.426 + // if this is not the first line, write out the 1.427 + // end of line characters from the previous line 1.428 + if (buf != null && eol1 != -1) { 1.429 + buf.write(eol1); 1.430 + if (eol2 != -1) 1.431 + buf.write(eol2); 1.432 + eol1 = eol2 = -1; 1.433 + } 1.434 + } 1.435 + 1.436 + // read the next byte 1.437 + if ((b = in.read()) < 0) { 1.438 + done = true; 1.439 + break; 1.440 + } 1.441 + 1.442 + /* 1.443 + * If we're at the end of the line, save the eol characters 1.444 + * to be written out before the beginning of the next line. 1.445 + */ 1.446 + if (b == '\r' || b == '\n') { 1.447 + bol = true; 1.448 + if (sin != null) 1.449 + end = sin.getPosition() - 1; 1.450 + eol1 = b; 1.451 + if (b == '\r') { 1.452 + in.mark(1); 1.453 + if ((b = in.read()) == '\n') 1.454 + eol2 = b; 1.455 + else 1.456 + in.reset(); 1.457 + } 1.458 + } else { 1.459 + bol = false; 1.460 + if (buf != null) 1.461 + buf.write(b); 1.462 + } 1.463 + } 1.464 + 1.465 + /* 1.466 + * Create a MimeBody element to represent this body part. 1.467 + */ 1.468 + MimeBodyPart part; 1.469 + if (sin != null) 1.470 + part = createMimeBodyPart(sin.newStream(start, end)); 1.471 + else 1.472 + part = createMimeBodyPart(headers, buf.getBytes(), buf.getCount()); 1.473 + addBodyPart(part); 1.474 + } 1.475 + } catch (IOException ioex) { 1.476 + throw new MessagingException("IO Error", ioex); 1.477 + } 1.478 + 1.479 + if (!ignoreMissingEndBoundary && !foundClosingBoundary && sin== null) { 1.480 + throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers"); 1.481 + } 1.482 + parsed = true; 1.483 + } 1.484 + 1.485 + /** 1.486 + * Create and return an InternetHeaders object that loads the 1.487 + * headers from the given InputStream. Subclasses can override 1.488 + * this method to return a subclass of InternetHeaders, if 1.489 + * necessary. This implementation simply constructs and returns 1.490 + * an InternetHeaders object. 1.491 + * 1.492 + * @param is the InputStream to read the headers from 1.493 + * @exception MessagingException 1.494 + * @since JavaMail 1.2 1.495 + */ 1.496 + protected InternetHeaders createInternetHeaders(InputStream is) 1.497 + throws MessagingException { 1.498 + return new InternetHeaders(is); 1.499 + } 1.500 + 1.501 + /** 1.502 + * Create and return a MimeBodyPart object to represent a 1.503 + * body part parsed from the InputStream. Subclasses can override 1.504 + * this method to return a subclass of MimeBodyPart, if 1.505 + * necessary. This implementation simply constructs and returns 1.506 + * a MimeBodyPart object. 1.507 + * 1.508 + * @param headers the headers for the body part 1.509 + * @param content the content of the body part 1.510 + * @since JavaMail 1.2 1.511 + */ 1.512 + protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, byte[] content, int len) { 1.513 + return new MimeBodyPart(headers, content,len); 1.514 + } 1.515 + 1.516 + /** 1.517 + * Create and return a MimeBodyPart object to represent a 1.518 + * body part parsed from the InputStream. Subclasses can override 1.519 + * this method to return a subclass of MimeBodyPart, if 1.520 + * necessary. This implementation simply constructs and returns 1.521 + * a MimeBodyPart object. 1.522 + * 1.523 + * @param is InputStream containing the body part 1.524 + * @exception MessagingException 1.525 + * @since JavaMail 1.2 1.526 + */ 1.527 + protected MimeBodyPart createMimeBodyPart(InputStream is) throws MessagingException { 1.528 + return new MimeBodyPart(is); 1.529 + } 1.530 + 1.531 + /** 1.532 + * Setup this MimeMultipart object from the given MultipartDataSource. <p> 1.533 + * 1.534 + * The method adds the MultipartDataSource's MimeBodyPart 1.535 + * objects into this MimeMultipart. This MimeMultipart's contentType is 1.536 + * set to that of the MultipartDataSource. <p> 1.537 + * 1.538 + * This method is typically used in those cases where one 1.539 + * has a multipart data source that has already been pre-parsed into 1.540 + * the individual body parts (for example, an IMAP datasource), but 1.541 + * needs to create an appropriate MimeMultipart subclass that represents 1.542 + * a specific multipart subtype. 1.543 + * 1.544 + * @param mp MimeMultipart datasource 1.545 + */ 1.546 + 1.547 + protected void setMultipartDataSource(MultipartDataSource mp) 1.548 + throws MessagingException { 1.549 + contentType = new ContentType(mp.getContentType()); 1.550 + 1.551 + int count = mp.getCount(); 1.552 + for (int i = 0; i < count; i++) 1.553 + addBodyPart(mp.getBodyPart(i)); 1.554 + } 1.555 + 1.556 + /** 1.557 + * Return the content-type of this MimeMultipart. <p> 1.558 + * 1.559 + * This implementation just returns the value of the 1.560 + * <code>contentType</code> field. 1.561 + * 1.562 + * @return content-type 1.563 + * @see #contentType 1.564 + */ 1.565 + public ContentType getContentType() { 1.566 + return contentType; 1.567 + } 1.568 + 1.569 + /** 1.570 + * Remove the specified part from the multipart message. 1.571 + * Shifts all the parts after the removed part down one. 1.572 + * 1.573 + * @param part The part to remove 1.574 + * @return true if part removed, false otherwise 1.575 + * @exception MessagingException if no such MimeBodyPart exists 1.576 + */ 1.577 + public boolean removeBodyPart(MimeBodyPart part) throws MessagingException { 1.578 + if (parts == null) 1.579 + throw new MessagingException("No such body part"); 1.580 + 1.581 + boolean ret = parts.remove(part); 1.582 + part.setParent(null); 1.583 + return ret; 1.584 + } 1.585 + 1.586 + /** 1.587 + * Remove the part at specified location (starting from 0). 1.588 + * Shifts all the parts after the removed part down one. 1.589 + * 1.590 + * @param index Index of the part to remove 1.591 + * @exception IndexOutOfBoundsException if the given index 1.592 + * is out of range. 1.593 + */ 1.594 + public void removeBodyPart(int index) { 1.595 + if (parts == null) 1.596 + throw new IndexOutOfBoundsException("No such BodyPart"); 1.597 + 1.598 + MimeBodyPart part = (MimeBodyPart)parts.get(index); 1.599 + parts.remove(index); 1.600 + part.setParent(null); 1.601 + } 1.602 + 1.603 + /** 1.604 + * Adds a MimeBodyPart to the multipart. The MimeBodyPart is appended to 1.605 + * the list of existing Parts. 1.606 + * 1.607 + * @param part The MimeBodyPart to be appended 1.608 + */ 1.609 + public synchronized void addBodyPart(MimeBodyPart part) { 1.610 + if (parts == null) 1.611 + parts = new FinalArrayList(); 1.612 + 1.613 + parts.add(part); 1.614 + part.setParent(this); 1.615 + } 1.616 + 1.617 + /** 1.618 + * Adds a MimeBodyPart at position <code>index</code>. 1.619 + * If <code>index</code> is not the last one in the list, 1.620 + * the subsequent parts are shifted up. If <code>index</code> 1.621 + * is larger than the number of parts present, the 1.622 + * MimeBodyPart is appended to the end. 1.623 + * 1.624 + * @param part The MimeBodyPart to be inserted 1.625 + * @param index Location where to insert the part 1.626 + */ 1.627 + public synchronized void addBodyPart(MimeBodyPart part, int index) { 1.628 + if (parts == null) 1.629 + parts = new FinalArrayList(); 1.630 + 1.631 + parts.add(index,part); 1.632 + part.setParent(this); 1.633 + } 1.634 + 1.635 + /** 1.636 + * Return the <code>MimeBodyPart</code> that contains this <code>MimeMultipart</code> 1.637 + * object, or <code>null</code> if not known. 1.638 + * @since JavaMail 1.1 1.639 + */ 1.640 + MimeBodyPart getParent() { 1.641 + return parent; 1.642 + } 1.643 + 1.644 + /** 1.645 + * Set the parent of this <code>MimeMultipart</code> to be the specified 1.646 + * <code>MimeBodyPart</code>. Normally called by the <code>Message</code> 1.647 + * or <code>MimeBodyPart</code> <code>setContent(MimeMultipart)</code> method. 1.648 + * <code>parent</code> may be <code>null</code> if the 1.649 + * <code>MimeMultipart</code> is being removed from its containing 1.650 + * <code>MimeBodyPart</code>. 1.651 + * @since JavaMail 1.1 1.652 + */ 1.653 + void setParent(MimeBodyPart parent) { 1.654 + this.parent = parent; 1.655 + } 1.656 +}