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