1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.orgchart;
15  
16  import java.awt.Component;
17  import java.awt.Graphics;
18  import java.awt.Graphics2D;
19  import java.awt.Image;
20  import java.awt.RenderingHints;
21  import java.io.IOException;
22  import java.net.URL;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.Map;
29  
30  import javax.swing.Icon;
31  import javax.swing.ImageIcon;
32  import javax.swing.tree.DefaultMutableTreeNode;
33  import javax.swing.tree.DefaultTreeModel;
34  import javax.xml.parsers.DocumentBuilderFactory;
35  import javax.xml.parsers.ParserConfigurationException;
36  
37  import org.w3c.dom.Document;
38  import org.w3c.dom.NamedNodeMap;
39  import org.w3c.dom.Node;
40  import org.w3c.dom.NodeList;
41  import org.xml.sax.InputSource;
42  import org.xml.sax.SAXException;
43  
44  /**
45   * TreeModel that uses {@link Employee}s as TreeNodes.
46   */
47  public class OrgChartTreeModel extends DefaultTreeModel {
48    
49    /**
50     * Creates a tree model with the given root.
51     */
52    public OrgChartTreeModel(Employee root) {
53      super(root);
54    }
55  
56    /**
57     * A TreeNode implementation that represents an Employee in an Organization.  
58     */
59    public static class Employee extends DefaultMutableTreeNode {
60      public String name;
61      public String email;
62      public String phone;
63      public String fax;
64      public String businessUnit;
65      public String position;
66      public String status;
67      public Icon icon;
68      public String assistant;    
69      public String layout;    
70    }
71    
72    /**
73     * Creates an instance of this class from an XML stream. 
74     * A sample XML file is located at resources/orgchartmodel.xml. 
75     */
76    public static OrgChartTreeModel create(InputSource input) {
77      OrgChartReader reader = new OrgChartReader();
78      return reader.read(input);    
79    }
80    
81    /**
82     * A reader for XML-formatted XML-files.
83     */
84    static class OrgChartReader {
85  
86      OrgChartTreeModel model;
87      
88      static Map userIcons;
89      static {    
90        userIcons = new HashMap();
91        for(int type = 0; type <= 1; type++) {
92          String gender = type == 0 ? "male" : "female";
93          for(int user = 1; user <= 3; user++) {
94            String key = "usericon_" + gender + user;
95            ArrayList urls = new ArrayList(4);
96            int size = 256;
97            //for(int size = 256; size <= 256; size *= 2) { 
98              urls.add(OrgChartReader.class.getResource("resources/icons/" + key + "_" + size + ".png"));
99            //}
100           userIcons.put(key,new MultiResolutionImageIcon(urls, 55,64));
101         }
102       };      
103     }
104     
105     public OrgChartTreeModel read(InputSource input) {
106       Document doc;
107       
108       try {
109         doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(input);
110         Employee treeRoot = visit(doc);
111         OrgChartTreeModel model = new OrgChartTreeModel(treeRoot);
112         return model;
113       } catch (SAXException e) {
114         e.printStackTrace();
115       } catch (IOException e) {
116         e.printStackTrace();
117       } catch (ParserConfigurationException e) {
118         e.printStackTrace();
119       }    
120       return null;
121     }
122     
123     public Employee visit(Node node)
124     {
125       String nodeName = node.getNodeName();
126       Employee employee = null;
127       if("employee".equals(nodeName)) {
128         employee = new Employee();        
129         
130         NamedNodeMap attributes = node.getAttributes();
131         Node attr = attributes.getNamedItem("name");      
132         if(attr != null) {
133           employee.name = attr.getNodeValue();
134         }
135         attr = attributes.getNamedItem("layout");      
136         if(attr != null) {
137           employee.layout = attr.getNodeValue();
138         }
139         attr = attributes.getNamedItem("email");      
140         if(attr != null) {
141           employee.email = attr.getNodeValue();
142         }
143         attr = attributes.getNamedItem("phone");      
144         if(attr != null) {
145           employee.phone = attr.getNodeValue();
146         }
147         attr = attributes.getNamedItem("position");      
148         if(attr != null) {
149           employee.position = attr.getNodeValue();
150         }    
151         attr = attributes.getNamedItem("fax");      
152         if(attr != null) {
153           employee.fax = attr.getNodeValue();
154         }
155         attr = attributes.getNamedItem("businessUnit");      
156         if(attr != null) {
157           employee.businessUnit = attr.getNodeValue();
158         }
159         attr = attributes.getNamedItem("status");      
160         if(attr != null) {
161           String status = attr.getNodeValue();
162           employee.status = status;                 
163         }
164         attr = attributes.getNamedItem("icon");      
165         if(attr != null) {
166           String iconName = attr.getNodeValue();
167           employee.icon = (Icon) userIcons.get(iconName);                
168         }
169         attr = attributes.getNamedItem("assistant");      
170         if(attr != null) {
171           employee.assistant = attr.getNodeValue();
172         }
173       }    
174       NodeList nl = node.getChildNodes();      
175       for(int i=0, cnt=nl.getLength(); i<cnt; i++)
176       {         
177         Node n = nl.item(i);
178         Employee childNode = visit(n);
179         if(childNode != null && employee != null) {
180           employee.add(childNode);        
181         }      
182         if(childNode != null && employee == null) {
183           return childNode;
184         }
185       }
186       return employee;
187     }
188   }
189   
190   /**
191    * Icon implementation that renders an image at a size that is 
192    * different than the image dimensions. 
193    */
194   static class FixedSizeImageIcon extends ImageIcon {
195         
196     int width;
197     int height;
198     
199     public FixedSizeImageIcon(URL imageURL, int width, int height) {    
200       super(imageURL);    
201       this.width = width;
202       this.height = height;
203     }
204     
205     public int getIconHeight() {
206       return height;
207     }
208 
209     public int getIconWidth() {
210       return width;
211     }
212 
213     public void paintIcon(Component c, Graphics gfx, int x, int y) {
214       Image image = getImage();    
215       Graphics2D g2d = (Graphics2D) gfx.create();
216       Object hint = g2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
217       g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);    
218       gfx.drawImage(image, x, y, getIconWidth(), getIconHeight(), c);     
219     }
220   }
221 
222   /**
223    * Icon implementation that displays one of multiple images depending 
224    * on the original image size and the current zoom applied to the
225    * graphics context. The idea is to pick an image whose original size 
226    * fits the screen size of the image best. 
227    */
228   static class MultiResolutionImageIcon implements Icon {
229     
230     int width;
231     int height;  
232     ArrayList entries;
233     
234     static class Entry {
235       double ratio;
236       Icon icon;    
237       URL url;
238     }
239     
240     public MultiResolutionImageIcon(URL[] imageURLs, int width, int height) {
241       this(Arrays.asList(imageURLs), width, height);
242     }
243     
244     public MultiResolutionImageIcon(Collection imageURLs, int width, int height) {    
245       entries = new ArrayList(imageURLs.size());
246       for(Iterator iter = imageURLs.iterator(); iter.hasNext(); ) {
247         URL imageURL = (URL) iter.next();
248         ImageIcon icon = new FixedSizeImageIcon(imageURL, width,height);      
249         Image image = icon.getImage();
250         double w = image.getWidth(null);
251         double h = image.getHeight(null);
252         double ratio = Math.min(w/width, h/height);
253         Entry entry = new Entry();
254         entry.ratio = ratio;
255         entry.icon = icon;
256         entry.url = imageURL;
257         entries.add(entry);
258       }        
259       this.width = width;
260       this.height = height;
261     }
262     
263     public int getIconHeight() {
264       return height;
265     }
266 
267     public int getIconWidth() {
268       return width;
269     }
270 
271     public void paintIcon(Component c, Graphics gfx, int x, int y) {
272       Graphics2D g2d = (Graphics2D) gfx;
273       double scale = g2d.getTransform().getScaleX();
274       Entry bestEntry = null;
275       double bestDelta = Double.MAX_VALUE;
276       for(int i = 0; i < entries.size(); i++) {
277         Entry entry = (Entry) entries.get(i);
278         double ratio = entry.ratio;
279         double delta = Math.abs(ratio-scale);
280         if(delta < bestDelta) {
281           bestDelta = delta;
282           bestEntry = entry;
283         }
284       }
285       if(bestEntry != null) {
286         bestEntry.icon.paintIcon(c, gfx, x,y);     
287       }
288     }
289   }
290 
291 }
292