1/* 2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 41 42import java.awt.Color; 43import java.awt.Font; 44import java.awt.GraphicsEnvironment; 45import java.util.Random; 46import javax.swing.tree.DefaultMutableTreeNode; 47 48 49/** 50 * DynamicTreeNode illustrates one of the possible ways in which dynamic 51 * loading can be used in tree. The basic premise behind this is that 52 * getChildCount() will be messaged from JTreeModel before any children 53 * are asked for. So, the first time getChildCount() is issued the 54 * children are loaded.<p> 55 * It should be noted that isLeaf will also be messaged from the model. 56 * The default behavior of TreeNode is to message getChildCount to 57 * determine this. As such, isLeaf is subclassed to always return false.<p> 58 * There are others ways this could be accomplished as well. Instead of 59 * subclassing TreeNode you could subclass JTreeModel and do the same 60 * thing in getChildCount(). Or, if you aren't using TreeNode you could 61 * write your own TreeModel implementation. 62 * Another solution would be to listen for TreeNodeExpansion events and 63 * the first time a node has been expanded post the appropriate insertion 64 * events. I would not recommend this approach though, the other two 65 * are much simpler and cleaner (and are faster from the perspective of 66 * how tree deals with it). 67 * 68 * NOTE: getAllowsChildren() can be messaged before getChildCount(). 69 * For this example the nodes always allow children, so it isn't 70 * a problem, but if you do support true leaf nodes you may want 71 * to check for loading in getAllowsChildren too. 72 * 73 * @author Scott Violet 74 */ 75@SuppressWarnings("serial") 76public class DynamicTreeNode extends DefaultMutableTreeNode { 77 // Class stuff. 78 79 /** Number of names. */ 80 protected static float nameCount; 81 /** Names to use for children. */ 82 protected static final String[] NAMES; 83 /** Potential fonts used to draw with. */ 84 protected static Font[] fonts; 85 /** Used to generate the names. */ 86 protected static Random nameGen; 87 /** Number of children to create for each node. */ 88 protected static final int DEFAULT_CHILDREN_COUNT = 7; 89 90 static { 91 String[] fontNames; 92 93 try { 94 fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment(). 95 getAvailableFontFamilyNames(); 96 97 } catch (Exception e) { 98 fontNames = null; 99 } 100 if (fontNames == null || fontNames.length == 0) { 101 NAMES = new String[] { "Mark Andrews", "Tom Ball", "Alan Chung", 102 "Rob Davis", "Jeff Dinkins", 103 "Amy Fowler", "James Gosling", 104 "David Karlton", "Dave Kloba", 105 "Dave Moore", "Hans Muller", 106 "Rick Levenson", "Tim Prinzing", 107 "Chester Rose", "Ray Ryan", 108 "Georges Saab", "Scott Violet", 109 "Kathy Walrath", "Arnaud Weber" }; 110 } else { 111 /* Create the Fonts, creating fonts is slow, much better to 112 do it once. */ 113 int fontSize = 12; 114 115 NAMES = fontNames; 116 fonts = new Font[NAMES.length]; 117 for (int counter = 0, maxCounter = NAMES.length; 118 counter < maxCounter; counter++) { 119 try { 120 fonts[counter] = new Font(fontNames[counter], 0, fontSize); 121 } catch (Exception e) { 122 fonts[counter] = null; 123 } 124 fontSize = ((fontSize + 2 - 12) % 12) + 12; 125 } 126 } 127 nameCount = (float) NAMES.length; 128 nameGen = new Random(System.currentTimeMillis()); 129 } 130 /** Have the children of this node been loaded yet? */ 131 protected boolean hasLoaded; 132 133 /** 134 * Constructs a new DynamicTreeNode instance with o as the user 135 * object. 136 */ 137 public DynamicTreeNode(Object o) { 138 super(o); 139 } 140 141 @Override 142 public boolean isLeaf() { 143 return false; 144 } 145 146 /** 147 * If hasLoaded is false, meaning the children have not yet been 148 * loaded, loadChildren is messaged and super is messaged for 149 * the return value. 150 */ 151 @Override 152 public int getChildCount() { 153 if (!hasLoaded) { 154 loadChildren(); 155 } 156 return super.getChildCount(); 157 } 158 159 /** 160 * Messaged the first time getChildCount is messaged. Creates 161 * children with random names from names. 162 */ 163 protected void loadChildren() { 164 DynamicTreeNode newNode; 165 Font font; 166 int randomIndex; 167 SampleData data; 168 169 for (int counter = 0; counter < DynamicTreeNode.DEFAULT_CHILDREN_COUNT; 170 counter++) { 171 randomIndex = (int) (nameGen.nextFloat() * nameCount); 172 String displayString = NAMES[randomIndex]; 173 if (fonts == null || fonts[randomIndex].canDisplayUpTo(displayString) 174 != -1) { 175 font = null; 176 } else { 177 font = fonts[randomIndex]; 178 } 179 180 if (counter % 2 == 0) { 181 data = new SampleData(font, Color.red, displayString); 182 } else { 183 data = new SampleData(font, Color.blue, displayString); 184 } 185 newNode = new DynamicTreeNode(data); 186 /* Don't use add() here, add calls insert(newNode, getChildCount()) 187 so if you want to use add, just be sure to set hasLoaded = true 188 first. */ 189 insert(newNode, counter); 190 } 191 /* This node has now been loaded, mark it so. */ 192 hasLoaded = true; 193 } 194} 195