1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.view.orgchart;
29  
30  import org.w3c.dom.Document;
31  import org.w3c.dom.Element;
32  import org.w3c.dom.NamedNodeMap;
33  import org.w3c.dom.Node;
34  import org.w3c.dom.NodeList;
35  import org.xml.sax.InputSource;
36  import org.xml.sax.SAXException;
37  
38  import javax.swing.Icon;
39  import javax.swing.ImageIcon;
40  import javax.swing.tree.DefaultMutableTreeNode;
41  import javax.swing.tree.DefaultTreeModel;
42  import javax.xml.parsers.DocumentBuilder;
43  import javax.xml.parsers.DocumentBuilderFactory;
44  import javax.xml.parsers.ParserConfigurationException;
45  import javax.xml.transform.OutputKeys;
46  import javax.xml.transform.Transformer;
47  import javax.xml.transform.TransformerConfigurationException;
48  import javax.xml.transform.TransformerException;
49  import javax.xml.transform.TransformerFactory;
50  import javax.xml.transform.dom.DOMSource;
51  import javax.xml.transform.stream.StreamResult;
52  import java.awt.Component;
53  import java.awt.Graphics;
54  import java.awt.Graphics2D;
55  import java.awt.Image;
56  import java.awt.RenderingHints;
57  import java.io.File;
58  import java.io.IOException;
59  import java.net.URL;
60  import java.util.Enumeration;
61  import java.util.HashMap;
62  import java.util.Iterator;
63  import java.util.Map;
64  import java.util.Set;
65  
66  /**
67   * TreeModel that uses {@link Employee}s as TreeNodes.
68   */
69  public class OrgChartTreeModel extends DefaultTreeModel {
70    
71    /**
72     * Creates a tree model with the given root.
73     */
74    public OrgChartTreeModel(final Employee root) {
75      super(root);
76    }
77  
78    /**
79     * A TreeNode implementation that represents an Employee in an Organization.  
80     */
81    public static class Employee extends DefaultMutableTreeNode {
82      public String name;
83      public String email;
84      public String phone;
85      public String fax;
86      public String businessUnit;
87      public String position;
88      public String status;
89      public Icon icon;
90      public boolean assistant;
91      public String layout;
92      public boolean vacant;
93  
94      /**
95       * Initializes a new employee instance.
96       */
97      public Employee() {
98        name = "";
99        email = "";
100       phone = "";
101       fax = "";
102       businessUnit = "";
103       position = "";
104       status = "";
105       layout = "";
106     }
107 
108     /**
109      * Sets the status of the position represented by this employee to vacant.
110      * Changes related properties.
111      */
112     public void vacate() {
113       vacant = true;
114       name = "";
115       status = "unavailable";
116       fax = "";
117       email = "";
118       phone = "";
119       icon = null;
120     }
121 
122     /**
123      * Adopts properties <code>layout</code>, <code>assistant</code>,
124      * <code>position</code>, and <code>businessUnit</code> from the
125      * given other employee.
126      */
127     public void adoptStructuralData( final Employee otherEmployee ) {
128       layout = otherEmployee.layout;
129       assistant = otherEmployee.assistant;
130       position = otherEmployee.position;
131       businessUnit = otherEmployee.businessUnit;
132     }
133   }
134   
135   /**
136    * Creates an instance of this class from an XML stream. 
137    * A sample XML file is located at resources/orgchartmodel.xml. 
138    */
139   public static OrgChartTreeModel create(final InputSource input) {
140     final OrgChartReader reader = new OrgChartReader();
141     return reader.read(input);    
142   }
143 
144   static class OrgChartWriter {
145   /**
146      * Writes current org chart to a file.
147      * @param selectedFile file chosen by user
148      * @param rootElement root element to start visiting
149      */
150     public void write(File selectedFile, final Employee rootElement) {
151       final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
152       final DocumentBuilder docBuilder;
153       try {
154         docBuilder = docFactory.newDocumentBuilder();
155         final Document doc = docBuilder.newDocument();
156         //get employees as DOM Elements
157         doc.appendChild(visit(rootElement,doc));
158 
159         // write the content into xml file
160         final TransformerFactory transformerFactory = TransformerFactory.newInstance();
161         final Transformer transformer = transformerFactory.newTransformer();
162         final DOMSource source = new DOMSource(doc);
163         //force xml as file extension
164         if (!selectedFile.getName().endsWith(".xml")) {
165           selectedFile = new File(selectedFile.getPath() + ".xml");
166         }
167         final StreamResult result = new StreamResult(selectedFile);
168         //nice output
169         transformer.setOutputProperty(OutputKeys.INDENT, "yes");
170         transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
171         transformer.transform(source, result);
172       } catch (ParserConfigurationException e) {
173         e.printStackTrace();
174       } catch (TransformerConfigurationException e) {
175         e.printStackTrace();
176       } catch (TransformerException e) {
177         e.printStackTrace();
178       }
179     }
180 
181     /**
182      * Visit an employee and its children.
183      * @param rootEmployee employee to start from
184      * @param doc document to create DOM nodes
185      * @return Employees as DOM tree
186      */
187     private Element visit(final Employee rootEmployee, final Document doc) {
188       final Element element = doc.createElement("employee");
189       element.setAttribute("name",rootEmployee.name);
190       element.setAttribute("email",rootEmployee.email);
191       element.setAttribute("phone",rootEmployee.phone);
192       element.setAttribute("fax",rootEmployee.fax);
193       element.setAttribute("businessUnit",rootEmployee.businessUnit);
194       element.setAttribute("position",rootEmployee.position);
195       element.setAttribute("status",rootEmployee.status);
196       element.setAttribute("assistant",Boolean.toString(rootEmployee.assistant));
197       element.setAttribute("vacant",Boolean.toString(rootEmployee.vacant));
198       element.setAttribute("layout",rootEmployee.layout);
199       if (rootEmployee.icon != null) {
200         element.setAttribute("icon",getIconName(rootEmployee.icon));
201       }
202       for(final Enumeration children = rootEmployee.children();children.hasMoreElements();) {
203         final Employee child = (Employee) children.nextElement();
204         element.appendChild(visit(child, doc));
205       }
206       return element;
207     }
208 
209     private String getIconName(final Icon element) {
210       final Set set = OrgChartReader.userIcons.keySet();
211       for(final Iterator iterator = set.iterator();iterator.hasNext();) {
212         final String iconName = (String) iterator.next();
213         if (OrgChartReader.userIcons.get(iconName) == element) {
214           return iconName;
215         }
216       }
217       //Should not be reached
218       return "";
219     }
220   }
221   
222   /**
223    * A reader for XML-formatted XML-files.
224    */
225   static class OrgChartReader {
226 
227     static Map userIcons;
228     static {    
229       userIcons = new HashMap();
230       for(int type = 0; type <= 1; type++) {
231         final String gender = type == 0 ? "male" : "female";
232         for(int user = 1; user <= 3; user++) {
233           final String key = "usericon_" + gender + user;
234           userIcons.put(key, new FixedSizeImageIcon(
235                   getResource("resource/" + key + "_256.png"), 56, 64));
236         }
237       }
238     }
239 
240     private static URL getResource( final String name ) {
241       return demo.view.DemoBase.getSharedResource(name);
242     }
243 
244     public OrgChartTreeModel read(final InputSource input) {
245       final Document doc;
246       
247       try {
248         doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(input);
249         final Employee treeRoot = visit(doc);
250         return new OrgChartTreeModel(treeRoot);
251       } catch (SAXException e) {
252         e.printStackTrace();
253       } catch (IOException e) {
254         e.printStackTrace();
255       } catch (ParserConfigurationException e) {
256         e.printStackTrace();
257       }    
258       return null;
259     }
260     
261     public Employee visit(final Node node)
262     {
263       final String nodeName = node.getNodeName();
264       Employee employee = null;
265       if("employee".equals(nodeName)) {
266         employee = new Employee();        
267         
268         final NamedNodeMap attributes = node.getAttributes();
269         Node attr = attributes.getNamedItem("name");
270         if(attr != null) {
271           employee.name = attr.getNodeValue();
272         }
273         attr = attributes.getNamedItem("layout");      
274         if(attr != null) {
275           employee.layout = attr.getNodeValue();
276         }
277         attr = attributes.getNamedItem("email");      
278         if(attr != null) {
279           employee.email = attr.getNodeValue();
280         }
281         attr = attributes.getNamedItem("phone");      
282         if(attr != null) {
283           employee.phone = attr.getNodeValue();
284         }
285         attr = attributes.getNamedItem("position");      
286         if(attr != null) {
287           employee.position = attr.getNodeValue();
288         }    
289         attr = attributes.getNamedItem("fax");      
290         if(attr != null) {
291           employee.fax = attr.getNodeValue();
292         }
293         attr = attributes.getNamedItem("businessUnit");      
294         if(attr != null) {
295           employee.businessUnit = attr.getNodeValue();
296         }
297         attr = attributes.getNamedItem("status");      
298         if(attr != null) {
299           employee.status = attr.getNodeValue();
300         }
301         attr = attributes.getNamedItem("icon");      
302         if(attr != null) {
303           final String iconName = attr.getNodeValue();
304           employee.icon = (Icon) userIcons.get(iconName);                
305         }
306         attr = attributes.getNamedItem("assistant");      
307         if(attr != null) {
308           employee.assistant = "true".equalsIgnoreCase(attr.getNodeValue());
309         }
310         attr = attributes.getNamedItem("vacant");
311         employee.vacant = (attr != null) && "true".equalsIgnoreCase(attr.getNodeValue());
312       }    
313       final NodeList nl = node.getChildNodes();
314       for(int i=0, cnt=nl.getLength(); i<cnt; i++)
315       {         
316         final Node n = nl.item(i);
317         final Employee childNode = visit(n);
318         if(childNode != null && employee != null) {
319           employee.add(childNode);
320         }      
321         if(childNode != null && employee == null) {
322           return childNode;
323         }
324       }
325       return employee;
326     }
327   }
328   
329   /**
330    * Icon implementation that renders an image at a size that is 
331    * different than the image dimensions. 
332    */
333   static class FixedSizeImageIcon extends ImageIcon {
334         
335     final int width;
336     final int height;
337     
338     public FixedSizeImageIcon(final URL imageURL, final int width, final int height) {
339       super(imageURL);    
340       this.width = width;
341       this.height = height;
342     }
343     
344     public int getIconHeight() {
345       return height;
346     }
347 
348     public int getIconWidth() {
349       return width;
350     }
351 
352     public void paintIcon(final Component c, final Graphics gfx, final int x, final int y) {
353       final Image image = getImage();
354       final Graphics2D g2d = (Graphics2D) gfx.create();
355       g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
356       g2d.drawImage(image, x, y, getIconWidth(), getIconHeight(), c);
357       g2d.dispose();
358     }
359   }
360 }
361