001/**************************************************************** 002 * Licensed to the Apache Software Foundation (ASF) under one * 003 * or more contributor license agreements. See the NOTICE file * 004 * distributed with this work for additional information * 005 * regarding copyright ownership. The ASF licenses this file * 006 * to you under the Apache License, Version 2.0 (the * 007 * "License"); you may not use this file except in compliance * 008 * with the License. You may obtain a copy of the License at * 009 * * 010 * http://www.apache.org/licenses/LICENSE-2.0 * 011 * * 012 * Unless required by applicable law or agreed to in writing, * 013 * software distributed under the License is distributed on an * 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 015 * KIND, either express or implied. See the License for the * 016 * specific language governing permissions and limitations * 017 * under the License. * 018 ****************************************************************/ 019 020package org.apache.james.mime4j.samples.tree; 021 022import java.awt.Dimension; 023import java.awt.GridLayout; 024import java.io.FileInputStream; 025import java.io.FileNotFoundException; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.Reader; 029import java.util.Date; 030import java.util.Map; 031 032import javax.swing.JFrame; 033import javax.swing.JPanel; 034import javax.swing.JScrollPane; 035import javax.swing.JSplitPane; 036import javax.swing.JTextArea; 037import javax.swing.JTree; 038import javax.swing.event.TreeSelectionEvent; 039import javax.swing.event.TreeSelectionListener; 040import javax.swing.tree.DefaultMutableTreeNode; 041import javax.swing.tree.TreeSelectionModel; 042 043import org.apache.james.mime4j.MimeException; 044import org.apache.james.mime4j.dom.BinaryBody; 045import org.apache.james.mime4j.dom.Body; 046import org.apache.james.mime4j.dom.Entity; 047import org.apache.james.mime4j.dom.Header; 048import org.apache.james.mime4j.dom.Message; 049import org.apache.james.mime4j.dom.MessageBuilder; 050import org.apache.james.mime4j.dom.Multipart; 051import org.apache.james.mime4j.dom.TextBody; 052import org.apache.james.mime4j.dom.address.Mailbox; 053import org.apache.james.mime4j.dom.address.MailboxList; 054import org.apache.james.mime4j.dom.field.AddressListField; 055import org.apache.james.mime4j.dom.field.ContentTypeField; 056import org.apache.james.mime4j.dom.field.DateTimeField; 057import org.apache.james.mime4j.dom.field.UnstructuredField; 058import org.apache.james.mime4j.field.address.AddressFormatter; 059import org.apache.james.mime4j.message.BodyPart; 060import org.apache.james.mime4j.message.MessageImpl; 061import org.apache.james.mime4j.message.DefaultMessageBuilder; 062import org.apache.james.mime4j.stream.Field; 063 064/** 065 * Displays a parsed Message in a window. The window will be divided into 066 * two panels. The left panel displays the Message tree. Clicking on a 067 * node in the tree shows information on that node in the right panel. 068 * 069 * Some of this code have been copied from the Java tutorial's JTree section. 070 */ 071public class MessageTree extends JPanel implements TreeSelectionListener { 072 private static final long serialVersionUID = 1L; 073 074 private JPanel contentPane; 075 private JTextArea textView; 076 private JTree tree; 077 078 /** 079 * Wraps an Object and associates it with a text. All message parts 080 * (headers, bodies, multiparts, body parts) will be wrapped in 081 * ObjectWrapper instances before they are added to the JTree instance. 082 */ 083 public static class ObjectWrapper { 084 private String text = ""; 085 private Object object = null; 086 087 public ObjectWrapper(String text, Object object) { 088 this.text = text; 089 this.object = object; 090 } 091 092 @Override 093 public String toString() { 094 return text; 095 } 096 097 public Object getObject() { 098 return object; 099 } 100 } 101 102 /** 103 * Creates a new <code>MessageTree</code> instance displaying the 104 * specified <code>Message</code>. 105 * 106 * @param message the message to display. 107 */ 108 public MessageTree(Message message) { 109 super(new GridLayout(1,0)); 110 111 DefaultMutableTreeNode root = createNode(message); 112 113 tree = new JTree(root); 114 tree.getSelectionModel().setSelectionMode( 115 TreeSelectionModel.SINGLE_TREE_SELECTION); 116 117 tree.addTreeSelectionListener(this); 118 119 JScrollPane treeView = new JScrollPane(tree); 120 121 contentPane = new JPanel(new GridLayout(1,0)); 122 JScrollPane contentView = new JScrollPane(contentPane); 123 124 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); 125 splitPane.setLeftComponent(treeView); 126 splitPane.setRightComponent(contentView); 127 128 Dimension minimumSize = new Dimension(100, 50); 129 contentView.setMinimumSize(minimumSize); 130 treeView.setMinimumSize(minimumSize); 131 splitPane.setDividerLocation(250); 132 splitPane.setPreferredSize(new Dimension(750, 500)); 133 134 add(splitPane); 135 136 textView = new JTextArea(); 137 textView.setEditable(false); 138 textView.setLineWrap(true); 139 contentPane.add(textView); 140 } 141 142 /** 143 * Create a node given a Multipart body. 144 * Add the Preamble, all Body parts and the Epilogue to the node. 145 * 146 * @param multipart the Multipart. 147 * @return the root node of the tree. 148 */ 149 private DefaultMutableTreeNode createNode(Header header) { 150 DefaultMutableTreeNode node = new DefaultMutableTreeNode( 151 new ObjectWrapper("Header", header)); 152 153 for (Field field : header.getFields()) { 154 String name = field.getName(); 155 156 node.add(new DefaultMutableTreeNode(new ObjectWrapper(name, field))); 157 } 158 159 return node; 160 } 161 162 /** 163 * Create a node given a Multipart body. 164 * Add the Preamble, all Body parts and the Epilogue to the node. 165 * 166 * @param multipart the Multipart. 167 * @return the root node of the tree. 168 */ 169 private DefaultMutableTreeNode createNode(Multipart multipart) { 170 DefaultMutableTreeNode node = new DefaultMutableTreeNode( 171 new ObjectWrapper("Multipart", multipart)); 172 173 node.add(new DefaultMutableTreeNode( 174 new ObjectWrapper("Preamble", multipart.getPreamble()))); 175 for (Entity part : multipart.getBodyParts()) { 176 node.add(createNode(part)); 177 } 178 node.add(new DefaultMutableTreeNode( 179 new ObjectWrapper("Epilogue", multipart.getEpilogue()))); 180 181 return node; 182 } 183 184 /** 185 * Creates the tree nodes given a MIME entity (either a Message or 186 * a BodyPart). 187 * 188 * @param entity the entity. 189 * @return the root node of the tree displaying the specified entity and 190 * its children. 191 */ 192 private DefaultMutableTreeNode createNode(Entity entity) { 193 194 /* 195 * Create the root node for the entity. It's either a 196 * Message or a Body part. 197 */ 198 String type = "Message"; 199 if (entity instanceof BodyPart) { 200 type = "Body part"; 201 } 202 DefaultMutableTreeNode node = new DefaultMutableTreeNode( 203 new ObjectWrapper(type, entity)); 204 205 /* 206 * Add the node encapsulating the entity Header. 207 */ 208 node.add(createNode(entity.getHeader())); 209 210 Body body = entity.getBody(); 211 212 if (body instanceof Multipart) { 213 /* 214 * The body of the entity is a Multipart. 215 */ 216 217 node.add(createNode((Multipart) body)); 218 } else if (body instanceof MessageImpl) { 219 /* 220 * The body is another Message. 221 */ 222 223 node.add(createNode((MessageImpl) body)); 224 225 } else { 226 /* 227 * Discrete Body (either of type TextBody or BinaryBody). 228 */ 229 type = "Text body"; 230 if (body instanceof BinaryBody) { 231 type = "Binary body"; 232 } 233 234 type += " (" + entity.getMimeType() + ")"; 235 node.add(new DefaultMutableTreeNode(new ObjectWrapper(type, body))); 236 237 } 238 239 return node; 240 } 241 242 /** 243 * Called whenever the selection changes in the JTree instance showing 244 * the Message nodes. 245 * 246 * @param e the event. 247 */ 248 public void valueChanged(TreeSelectionEvent e) { 249 DefaultMutableTreeNode node = (DefaultMutableTreeNode) 250 tree.getLastSelectedPathComponent(); 251 252 textView.setText(""); 253 254 if (node == null) { 255 return; 256 } 257 258 Object o = ((ObjectWrapper) node.getUserObject()).getObject(); 259 260 if (node.isLeaf()) { 261 262 if (o instanceof TextBody){ 263 /* 264 * A text body. Display its contents. 265 */ 266 TextBody body = (TextBody) o; 267 StringBuilder sb = new StringBuilder(); 268 try { 269 Reader r = body.getReader(); 270 int c; 271 while ((c = r.read()) != -1) { 272 sb.append((char) c); 273 } 274 } catch (IOException ex) { 275 ex.printStackTrace(); 276 } 277 textView.setText(sb.toString()); 278 279 } else if (o instanceof BinaryBody){ 280 /* 281 * A binary body. Display its MIME type and length in bytes. 282 */ 283 BinaryBody body = (BinaryBody) o; 284 int size = 0; 285 try { 286 InputStream is = body.getInputStream(); 287 while ((is.read()) != -1) { 288 size++; 289 } 290 } catch (IOException ex) { 291 ex.printStackTrace(); 292 } 293 textView.setText("Binary body\n" 294 + "MIME type: " 295 + body.getParent().getMimeType() + "\n" 296 + "Size of decoded data: " + size + " bytes"); 297 298 } else if (o instanceof ContentTypeField) { 299 /* 300 * Content-Type field. 301 */ 302 ContentTypeField field = (ContentTypeField) o; 303 StringBuilder sb = new StringBuilder(); 304 sb.append("MIME type: " + field.getMimeType() + "\n"); 305 for (Map.Entry<String, String> entry : field.getParameters().entrySet()) { 306 sb.append(entry.getKey() + " = " + entry.getValue() + "\n"); 307 } 308 textView.setText(sb.toString()); 309 310 } else if (o instanceof AddressListField) { 311 /* 312 * An address field (From, To, Cc, etc) 313 */ 314 AddressListField field = (AddressListField) o; 315 MailboxList list = field.getAddressList().flatten(); 316 StringBuilder sb = new StringBuilder(); 317 for (int i = 0; i < list.size(); i++) { 318 Mailbox mb = list.get(i); 319 sb.append(AddressFormatter.DEFAULT.format(mb, false) + "\n"); 320 } 321 textView.setText(sb.toString()); 322 323 } else if (o instanceof DateTimeField) { 324 Date date = ((DateTimeField) o).getDate(); 325 textView.setText(date.toString()); 326 } else if (o instanceof UnstructuredField){ 327 textView.setText(((UnstructuredField) o).getValue()); 328 } else if (o instanceof Field){ 329 textView.setText(((Field) o).getBody()); 330 } else { 331 /* 332 * The Object should be a Header or a String containing a 333 * Preamble or Epilogue. 334 */ 335 textView.setText(o.toString()); 336 } 337 338 } 339 } 340 341 /** 342 * Creates and displays the gui. 343 * 344 * @param message the Message to display in the tree. 345 */ 346 private static void createAndShowGUI(Message message) { 347 /* 348 * Create and set up the window. 349 */ 350 JFrame frame = new JFrame("MessageTree"); 351 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 352 353 /* 354 * Create and set up the content pane. 355 */ 356 MessageTree newContentPane = new MessageTree(message); 357 newContentPane.setOpaque(true); 358 frame.setContentPane(newContentPane); 359 360 /* 361 * Display the window. 362 */ 363 frame.pack(); 364 frame.setVisible(true); 365 } 366 367 public static void main(String[] args) { 368 try { 369 final MessageBuilder builder = new DefaultMessageBuilder(); 370 final Message message = builder.parseMessage(new FileInputStream(args[0])); 371 372 javax.swing.SwingUtilities.invokeLater(new Runnable() { 373 public void run() { 374 createAndShowGUI(message); 375 } 376 }); 377 378 } catch (ArrayIndexOutOfBoundsException e) { 379 System.err.println("Wrong number of arguments."); 380 System.err.println("Usage: org.mime4j.samples.tree.MessageTree" 381 + " path/to/message"); 382 } catch (FileNotFoundException e) { 383 System.err.println("The file '" + args[0] + "' could not be found."); 384 } catch (IOException e) { 385 System.err.println("The file '" + args[0] + "' could not be read."); 386 } catch (MimeException e) { 387 System.err.println("The file '" + args[0] + "' is invalid."); 388 } 389 } 390 391}