001: /*
002: * $Header: /cvs/j3dfly/J3dEditor/src/org/jdesktop/j3dedit/scenegrapheditor/ConfigLoader.java,v 1.1 2005/04/20 22:20:44 paulby Exp $
003: *
004: * Sun Public License Notice
005: *
006: * The contents of this file are subject to the Sun Public License Version
007: * 1.0 (the "License"). You may not use this file except in compliance with
008: * the License. A copy of the License is available at http://www.sun.com/
009: *
010: * The Original Code is the Java 3D(tm) Scene Graph Editor.
011: * The Initial Developer of the Original Code is Paul Byrne.
012: * Portions created by Paul Byrne are Copyright (C) 2002.
013: * All Rights Reserved.
014: *
015: * Contributor(s): Paul Byrne, Jan Becicka
016: *
017: **/
018: package org.jdesktop.j3dedit.scenegrapheditor;
019:
020: import java.io.StreamTokenizer;
021: import java.io.InputStreamReader;
022: import java.net.URL;
023: import javax.swing.JPanel;
024: import javax.swing.JMenuItem;
025: import javax.swing.JMenu;
026: import javax.swing.JTabbedPane;
027: import javax.swing.JButton;
028: import javax.swing.ImageIcon;
029: import java.awt.event.ActionListener;
030: import java.awt.MediaTracker;
031: import java.util.HashMap;
032: import java.util.ArrayList;
033: import java.awt.MediaTracker;
034: import java.awt.Image;
035: import javax.xml.parsers.*;
036: import org.w3c.dom.*;
037:
038: import org.jdesktop.j3dedit.scenegrapheditor.nodeeditors.EditorManager;
039:
040: /**
041: * Loads and parses the Configuration for the Nodes in the Scene Graph.
042: *
043: * @author Paul Byrne
044: * @version $Revision: 1.1 $
045: */
046: public class ConfigLoader extends Object {
047:
048: private ConfigLoader configLoader = null;
049:
050: private JMenu menuRoot; // Root menu item for addchild
051: private NodeCreationListener creationListener;
052: private J3dNodesToolbar j3dNodesToolbar;
053: private JTabbedPane tabPane;
054:
055: private EditorManager editorManager; // The editor manager
056: private HashMap tabPanelNames; // Mapping of tab name to panel
057: private TreeIconControl treeIconControl;
058:
059: private ArrayList icons = new ArrayList();
060: private InternalListener internalListener = new InternalListener();
061:
062: private String defaultConstructorClass = "scenegrapheditor.DefaultNodeConstructors";
063:
064: private Document document; // DOM document - representation of config file
065: private java.net.URL url; //url to config file
066:
067: /** Holds value of property generatorTable. */
068: private java.util.Hashtable generatorTable;
069:
070: public ConfigLoader(java.net.URL url) {
071: this (null, new JMenu("Add Child"), url);
072: }
073:
074: /**
075: * Create a new ConfigLoader and read the config file.
076: *
077: * The tabPane will be populated with all the node creation icons and
078: * the addChild menu will be populated
079: */
080: public ConfigLoader() {
081: this (null, new JMenu("Add Child"));
082: }
083:
084: protected ConfigLoader(NodeCreationListener creationListener,
085: JMenu addChildMenu) {
086: this (creationListener, addChildMenu, null);
087: }
088:
089: /** Creates new PreferenceLoader
090: * If URL==null then the method will use the path defined in the property
091: * J3dEditor.NodeConfigs.
092: */
093: protected ConfigLoader(NodeCreationListener creationListener,
094: JMenu addChildMenu, java.net.URL urlIn) {
095: this .url = urlIn;
096: generatorTable = new java.util.Hashtable();
097:
098: if (url == null) {
099: try {
100: url = new java.net.URL("file:"
101: + System.getProperty("J3dEditor.NodeConfigs"));
102: } catch (Exception e) {
103: org.jdesktop.j3dfly.utils.gui.ErrorManager
104: .getDefault()
105: .notify(
106: e,
107: org.jdesktop.j3dfly.utils.gui.ErrorHandler.ERROR,
108: "Unable to load NodeConfigs from J3dEditor.NodeConfigs property setting of "
109: + System
110: .getProperty("J3dEditor.NodeConfigs"));
111: }
112: }
113:
114: menuRoot = addChildMenu;
115: this .creationListener = creationListener;
116: j3dNodesToolbar = new org.jdesktop.j3dedit.scenegrapheditor.J3dNodesToolbar();
117:
118: if (configLoader != null)
119: throw new RuntimeException(
120: "Multiple ConfigLoaders instantiated");
121:
122: configLoader = this ;
123:
124: treeIconControl = new TreeIconControl();
125:
126: tabPanelNames = new HashMap();
127: for (int i = 0; i < j3dNodesToolbar.getTabPane().getTabCount(); i++)
128: tabPanelNames.put(j3dNodesToolbar.getTabPane()
129: .getTitleAt(i), j3dNodesToolbar.getTabPane()
130: .getComponentAt(i));
131:
132: editorManager = new EditorManager();
133:
134: try {
135: parseXML(url.openStream(), this .getClass().getClassLoader());
136: } catch (java.io.IOException e) {
137: org.jdesktop.j3dfly.utils.gui.ErrorManager
138: .getDefault()
139: .notify(
140: e,
141: org.jdesktop.j3dfly.utils.gui.ErrorHandler.ERROR,
142: "Unable to load Config file " + url);
143: }
144:
145: j3dNodesToolbar.groupSelected(false);
146: }
147:
148: /**
149: * Add to the config by parsing the xml in the stream provided
150: */
151: public void addConfig(java.io.InputStream in,
152: ClassLoader classLoader) {
153: parseXML(in, classLoader);
154: }
155:
156: private void parseXML(java.io.InputStream in,
157: ClassLoader classLoader) {
158: try {
159: DocumentBuilder db = DocumentBuilderFactory.newInstance()
160: .newDocumentBuilder();
161: org.xml.sax.InputSource is = new org.xml.sax.InputSource(in);
162:
163: // TODO I think this next line should point to the dtd as a resource
164: // instead of assuming the dtd and url are co-located
165: is.setSystemId(url.toString());
166:
167: document = db.parse(is);
168: } catch (java.io.IOException e) {
169: org.jdesktop.j3dfly.utils.gui.ErrorManager
170: .getDefault()
171: .notify(
172: e,
173: org.jdesktop.j3dfly.utils.gui.ErrorHandler.ERROR,
174: "Unable to load Config file "
175: + url
176: + " Check J3dEditor.NodeConfigs java property");
177: } catch (ParserConfigurationException e) {
178: org.jdesktop.j3dfly.utils.gui.ErrorManager
179: .getDefault()
180: .notify(
181: e,
182: org.jdesktop.j3dfly.utils.gui.ErrorHandler.ERROR);
183: } catch (org.xml.sax.SAXException e) {
184: org.jdesktop.j3dfly.utils.gui.ErrorManager
185: .getDefault()
186: .notify(
187: e,
188: org.jdesktop.j3dfly.utils.gui.ErrorHandler.ERROR);
189: }
190: visitDocument(classLoader);
191: }
192:
193: public ConfigLoader getConfigLoader() {
194: if (configLoader == null)
195: new ConfigLoader();
196: return configLoader;
197: }
198:
199: /**
200: * Return a menu with all the nodes that can be added to the scene graph.
201: * The menu items all have listeners attached which will fire when
202: * an item is selected
203: */
204: public JMenu getAddChildMenu() {
205: return menuRoot;
206: }
207:
208: /**
209: * Return the J3dNodeToolbar configured by the config file
210: */
211: public J3dNodesToolbar getJ3dNodesToolbar() {
212: return j3dNodesToolbar;
213: }
214:
215: /**
216: * Set the node creation listener that will be notified when
217: * a user action causes a node to be created
218: */
219: public void setNodeCreationListener(NodeCreationListener listener) {
220: internalListener.actualListener = listener;
221: }
222:
223: /**
224: * Returns the EditorManager
225: */
226: public EditorManager getEditorManager() {
227: return editorManager;
228: }
229:
230: /**
231: * Get the tree icon control object
232: */
233: public TreeIconControl getTreeIconControl() {
234: return treeIconControl;
235: }
236:
237: private void addTab(String tabPanelName, JButton iconButton) {
238: JPanel p = (JPanel) tabPanelNames.get(tabPanelName);
239: if (p == null) {
240: p = new JPanel();
241: j3dNodesToolbar.getTabPane().add(tabPanelName, p);
242: tabPanelNames.put(tabPanelName, p);
243: }
244:
245: p.add(iconButton);
246: }
247:
248: /**
249: * Create the an Action
250: * Attach to the menuItem and the button
251: */
252: private void addAction(javax.swing.JMenuItem menuItem,
253: JButton iconButton, String nodeClassName,
254: String constructorClass, String constructorMethod,
255: boolean autoName, ClassLoader classLoader) {
256: Class nodeClass;
257: ActionListener actionListener;
258:
259: try {
260: nodeClass = classLoader.loadClass(nodeClassName);
261: } catch (ClassNotFoundException e) {
262: System.out
263: .println("Invalid Java3D Node class in config file");
264: System.out.println(nodeClassName);
265: return;
266: }
267:
268: if (constructorMethod == null)
269: actionListener = new SimpleNodeCreatorAction(nodeClass,
270: internalListener, autoName);
271: else
272: actionListener = new AdvancedNodeCreatorAction(
273: internalListener, constructorClass,
274: constructorMethod, autoName, classLoader);
275:
276: menuItem.addActionListener(actionListener);
277:
278: if (iconButton != null)
279: iconButton.addActionListener(actionListener);
280: }
281:
282: private JMenuItem parseMenu(String menu) {
283: java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
284: menu, " ");
285: String item;
286: JMenuItem ret = null;
287: JMenu currentMenu = menuRoot;
288:
289: while (tokenizer.hasMoreTokens()) {
290: item = tokenizer.nextToken();
291: if (!tokenizer.hasMoreTokens()) {
292: // item is JMenuItem
293: ret = new JMenuItem(item);
294: currentMenu.add(ret);
295: } else {
296: // item is JMenu
297:
298: // Check for duplicate entry
299: JMenu tmp = null;
300: for (int i = 0; i < currentMenu.getItemCount()
301: && tmp == null; i++)
302: if (currentMenu.getItem(i) instanceof JMenu
303: && currentMenu.getItem(i).getText().equals(
304: item))
305: tmp = (JMenu) currentMenu.getItem(i);
306:
307: if (tmp == null) { // Make new entry
308: tmp = new JMenu(item);
309: currentMenu.add(tmp);
310: }
311: currentMenu = tmp;
312: }
313: }
314:
315: return ret;
316: }
317:
318: /** Scan through org.w3c.dom.Document document. */
319: public void visitDocument(ClassLoader classLoader) {
320: org.w3c.dom.Element element = document.getDocumentElement();
321: if ((element != null)
322: && element.getTagName().equals("Configuration")) {
323: visitElement_Configuration(element, classLoader);
324: }
325: if ((element != null) && element.getTagName().equals("Node")) {
326: visitElement_Node(element, classLoader);
327: }
328: }
329:
330: /** Scan through org.w3c.dom.Element named Configuration. */
331: void visitElement_Configuration(org.w3c.dom.Element element,
332: ClassLoader classLoader) { // <Configuration>
333: // element.getValue();
334: org.w3c.dom.NodeList nodes = element.getChildNodes();
335: for (int i = 0; i < nodes.getLength(); i++) {
336: org.w3c.dom.Node node = nodes.item(i);
337: switch (node.getNodeType()) {
338: case org.w3c.dom.Node.CDATA_SECTION_NODE:
339: // ((org.w3c.dom.CDATASection)node).getData();
340: break;
341: case org.w3c.dom.Node.ELEMENT_NODE:
342: org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
343: if (nodeElement.getTagName().equals("Node")) {
344: visitElement_Node(nodeElement, classLoader);
345: }
346: break;
347: case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
348: // ((org.w3c.dom.ProcessingInstruction)node).getTarget();
349: // ((org.w3c.dom.ProcessingInstruction)node).getData();
350: break;
351: }
352: }
353: }
354:
355: /** Scan through org.w3c.dom.Element named Node. */
356: void visitElement_Node(org.w3c.dom.Element element,
357: ClassLoader classLoader) { // <Node>
358: // element.getValue();
359: String nodeName = null;
360: String icon = null;
361: String icon19 = null;
362: String editorClassName = null;
363: String constructorClassName = defaultConstructorClass;
364: String constructorMethodName = null;
365: String tabName = null;
366: String generator = null;
367: boolean autoName = false;
368: JMenuItem menuItem = null;
369: java.awt.Insets marginInsets = new java.awt.Insets(0, 0, 0, 0);
370: MediaTracker mediaTracker;
371: java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();
372:
373: org.w3c.dom.NamedNodeMap attrs = element.getAttributes();
374: for (int i = 0; i < attrs.getLength(); i++) {
375: org.w3c.dom.Attr attr = (org.w3c.dom.Attr) attrs.item(i);
376: if (attr.getName().equals("class")) { // <Node class="???">
377: nodeName = attr.getValue();
378: }
379: if (attr.getName().equals("editor")) { // <Node editor="???">
380: editorClassName = attr.getValue();
381: }
382: if (attr.getName().equals("generator")) { // <Node generator="???">
383: generator = attr.getValue();
384: }
385: if (attr.getName().equals("menu")) { // <Node menu="???">
386: menuItem = parseMenu(attr.getValue());
387: }
388: if (attr.getName().equals("icon")) { // <Node icon="???">
389: icon = attr.getValue();
390: }
391: if (attr.getName().equals("icon19")) { // <Node icon19="???">
392: icon19 = attr.getValue();
393: }
394: if (attr.getName().equals("tab_name")) { // <Node tab_name="???">
395: tabName = attr.getValue();
396: }
397: if (attr.getName().equals("constructor_class")) { // <Node constructor_class="???">
398: constructorClassName = attr.getValue();
399: }
400: if (attr.getName().equals("constructor_method")) { // <Node constructor_method="???">
401: constructorMethodName = attr.getValue();
402: }
403: if (attr.getName().equals("auto_name")) { // <Node auto_name="???">
404: autoName = Boolean.valueOf(attr.getValue())
405: .booleanValue();
406: }
407: }
408:
409: //process node
410:
411: JButton iconButton = null;
412:
413: if (icon19 != null && tabName != null) {
414: try {
415: ImageIcon imageIcon = new ImageIcon(
416: javax.imageio.ImageIO.read(classLoader
417: .getResourceAsStream(icon19)));
418: icons.add(imageIcon);
419: iconButton = new JButton(imageIcon);
420: iconButton.setMargin(marginInsets);
421: iconButton.setToolTipText(nodeName);
422: addTab(tabName, iconButton);
423: } catch (Exception e) {
424: System.out.println("Can't create iconButton for "
425: + editorClassName);
426: System.out.println("Bad resource " + icon19);
427: }
428: }
429:
430: if (editorClassName != null) {
431: try {
432: Class editorClass = classLoader
433: .loadClass(editorClassName);
434: editorManager.setEditorClass(nodeName, editorClass);
435:
436: if (icon != null) {
437: try {
438: treeIconControl.addImage(classLoader
439: .loadClass(nodeName),
440: javax.imageio.ImageIO.read(classLoader
441: .getResourceAsStream(icon)));
442: } catch (java.io.IOException ioE) {
443: System.out.println("Unable to load icon "
444: + icon);
445: }
446: }
447: } catch (ClassNotFoundException e) {
448: System.out.println("Error in config file");
449: System.out.println("Either, Java3D Node " + nodeName
450: + " not in classpath");
451: System.out.println("Or Editor Class " + editorClassName
452: + " not in classpath");
453: }
454: }
455:
456: if (menuItem != null)
457: addAction(menuItem, iconButton, nodeName,
458: constructorClassName, constructorMethodName,
459: autoName, classLoader);
460: if (generator != null) {
461: generatorTable.put(nodeName, generator);
462: }
463: }
464:
465: /** Getter for property generatorTable.
466: * @return Value of property generatorTable.
467: */
468: public java.util.Hashtable getGeneratorTable() {
469: return generatorTable;
470: }
471:
472: }
473:
474: class SimpleNodeCreatorAction implements java.awt.event.ActionListener {
475:
476: private Class nodeClass;
477: private boolean autoName;
478: private NodeCreationListener listener;
479:
480: public SimpleNodeCreatorAction(Class nodeClass,
481: NodeCreationListener listener, boolean autoName) {
482: this .nodeClass = nodeClass;
483: this .listener = listener;
484: this .autoName = autoName;
485:
486: // Check that a default constructor is available
487: try {
488: java.lang.reflect.Constructor constructor;
489: constructor = nodeClass.getConstructor((Class[]) null);
490: } catch (NoSuchMethodException e) {
491: System.out.println("Error in config");
492: System.out.println("Java3D Node " + nodeClass.getName()
493: + " does not have a default constructor");
494: }
495: }
496:
497: public void actionPerformed(java.awt.event.ActionEvent evt) {
498: try {
499: listener.nodeCreated((javax.media.j3d.Node) nodeClass
500: .newInstance(), autoName);
501: } catch (Exception e) {
502: System.out.println("Error Creating Node");
503: e.printStackTrace();
504: }
505: }
506:
507: }
508:
509: class AdvancedNodeCreatorAction implements
510: java.awt.event.ActionListener {
511:
512: private NodeCreationListener listener;
513: private boolean autoName;
514: private Class constructorClass;
515: private java.lang.reflect.Method constructorMethod;
516:
517: public AdvancedNodeCreatorAction(NodeCreationListener listener,
518: String constructorClassName, String constructorMethodName,
519: boolean autoName, ClassLoader classLoader) {
520:
521: this .listener = listener;
522: this .autoName = autoName;
523:
524: try {
525: constructorClass = classLoader
526: .loadClass(constructorClassName);
527: constructorMethod = constructorClass.getMethod(
528: constructorMethodName, (Class[]) null);
529: } catch (ClassNotFoundException e) {
530: System.out.println("Error in config file");
531: System.out.println("No Such constructor class "
532: + constructorClassName);
533: } catch (NoSuchMethodException ex) {
534: System.out.println("Error in config file");
535: System.out.println("No Such constructor Method "
536: + constructorMethodName + " in class "
537: + constructorClassName);
538: }
539: }
540:
541: public void actionPerformed(java.awt.event.ActionEvent evt) {
542: try {
543: listener.nodeCreated(
544: (javax.media.j3d.Node) constructorMethod.invoke(
545: constructorClass, (Object[]) null),
546: autoName);
547: } catch (Exception e) {
548: System.out.println("Unable to create node");
549: e.printStackTrace();
550: }
551: }
552:
553: }
554:
555: class InternalListener implements NodeCreationListener {
556:
557: NodeCreationListener actualListener;
558:
559: /**
560: * Called when the user creates a new node
561: */
562: public void nodeCreated(javax.media.j3d.Node node, boolean autoName) {
563: actualListener.nodeCreated(node, autoName);
564: }
565: }
|