package jas2.util.xmlmenus;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import jas2.util.*;
import jas2.util.layout.ToolbarLayout;
import jas2.util.xml.*;
import org.w3c.dom.*;
public class XMLMenuBuilder
{
/**
* Create am XMLMenuBuilder by reading an XML file
* @param in The XML input
* @param fileName The file name (for error messages)
*/
public XMLMenuBuilder(Reader in, String fileName) throws JASDOMParser.JASXMLException
{
defaultMenuBarClass = JMenuBar.class;
defaultMenuClass = JMenu.class;
defaultMenuItemClass = JMenuItem.class;
defaultRadioMenuItemClass = JRadioButtonMenuItem.class;
defaultCheckboxMenuItemClass = JCheckBoxMenuItem.class;
defaultMenuSeparatorClass = JSeparator.class;
defaultPopupMenuClass = JPopupMenu.class;
defaultPopupMenuItemClass = defaultMenuItemClass;
defaultPopupRadioMenuItemClass = defaultRadioMenuItemClass;
defaultPopupCheckboxMenuItemClass = defaultCheckboxMenuItemClass;
defaultPopupMenuSeparatorClass = JPopupMenu.Separator.class;
defaultToolBarClass = JToolBar.class;
defaultToolBarItemClass = JButton.class;
defaultToolBarSeparatorClass = JToolBar.Separator.class;
defaultImageIconClass = JASIcon.class;
ClassPathEntityResolver er = new ClassPathEntityResolver("menus.dtd",XMLMenuBuilder.class);
JASDOMParser parser = JASDOMParser.instance();
document = parser.parse(in,fileName,er);
}
/**
* Exception to throw when the result of bad XML (something that should not have gotten past a validating parser) is encountered.
*/
public static class BadXMLException extends RuntimeException
{
public BadXMLException() { super(); }
public BadXMLException(String s) { super(s); }
}
/**
* Exception to throw when the client programmer tries to set a default class of something to be a class
* that cannot be upcast to the thing's current default class.
*/
public static class IllegalDefaultClassException extends RuntimeException
{
public IllegalDefaultClassException() { super(); }
public IllegalDefaultClassException(String s) { super(s); }
}
/**
* sets the default class of menubars to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultMenuBarClass(Class klass) throws IllegalDefaultClassException
{
defaultMenuBarClass = setDefaultClass(defaultMenuBarClass, klass);
}
/**
* sets the default class of menus to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultMenuClass(Class klass) throws IllegalDefaultClassException
{
defaultMenuClass = setDefaultClass(defaultMenuClass, klass);
}
/**
* sets the default class of menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultMenuItemClass(Class klass) throws IllegalDefaultClassException
{
defaultMenuItemClass = setDefaultClass(defaultMenuItemClass, klass);
}
/**
* sets the default class of radio menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultRadioMenuItemClass(Class klass) throws IllegalDefaultClassException
{
defaultRadioMenuItemClass = setDefaultClass(defaultRadioMenuItemClass, klass);
}
/**
* sets the default class of checkbox menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultCheckboxMenuItemClass(Class klass) throws IllegalDefaultClassException
{
defaultCheckboxMenuItemClass = setDefaultClass(defaultCheckboxMenuItemClass, klass);
}
/**
* sets the default class of menu separator items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultMenuSeparatorClass(Class klass) throws IllegalDefaultClassException
{
defaultMenuSeparatorClass = setDefaultClass(defaultMenuSeparatorClass, klass);
}
/**
* sets the default class of popup menus to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultPopupMenuClass(Class klass) throws IllegalDefaultClassException
{
defaultPopupMenuClass = setDefaultClass(defaultPopupMenuClass, klass);
}
/**
* sets the default class of popup menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultPopupMenuItemClass(Class klass) throws IllegalDefaultClassException
{
defaultPopupMenuItemClass = setDefaultClass(defaultPopupMenuItemClass, klass);
}
/**
* sets the default class of popup radio menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultPopupRadioMenuItemClass(Class klass) throws IllegalDefaultClassException
{
defaultPopupRadioMenuItemClass = setDefaultClass(defaultPopupRadioMenuItemClass, klass);
}
/**
* sets the default class of popup checkbox menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultPopupCheckboxMenuItemClass(Class klass) throws IllegalDefaultClassException
{
defaultPopupCheckboxMenuItemClass = setDefaultClass(defaultPopupCheckboxMenuItemClass, klass);
}
/**
* sets the default class of popup menu separator items to be klass, which MUST be the default class specified in the constructorr or one of its subclasses
* @param klass the new default class
*/
public void setDefaultPopupMenuSeparatorClass(Class klass) throws IllegalDefaultClassException
{
defaultPopupMenuSeparatorClass = setDefaultClass(defaultPopupMenuSeparatorClass, klass);
}
/**
* sets the default class of toolbars to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultToolBarClass(Class klass) throws IllegalDefaultClassException
{
defaultToolBarClass = setDefaultClass(defaultToolBarClass, klass);
}
/**
* sets the default class of menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultToolBarItemClass(Class klass) throws IllegalDefaultClassException
{
defaultToolBarItemClass = setDefaultClass(defaultToolBarItemClass, klass);
}
/**
* sets the default class of menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultToolBarSeparatorClass(Class klass) throws IllegalDefaultClassException
{
defaultToolBarSeparatorClass = setDefaultClass(defaultToolBarSeparatorClass, klass);
}
/**
* sets the default class of menu items to be klass, which MUST be the default class specified in the constructor or one of its subclasses
* @param klass the new default class
*/
public void setDefaultImageIconClass(Class klass) throws IllegalDefaultClassException
{
defaultImageIconClass = setDefaultClass(defaultImageIconClass, klass);
}
/**
* get the menu bar(and its contents) with ID id
*/
public JMenuBar getMenuBar(String id)
{
return (JMenuBar)(getIt("menuBar", "id", id));
}
/**
* get the tool bar (and its contents) with ID id
*/
public JToolBar getToolBar(String id)
{
return (JToolBar)(getIt("toolBar", "id", id));
}
/**
* get the popup menu (and its contents) with ID id
*/
public JPopupMenu getPopupMenu(String id)
{
return (JPopupMenu)(getIt("popupMenu", "id", id));
}
//
// private methods
//
/**
* get the Component with ID id and set its name to be id while we're at it
*/
private Component getIt(String tag, String whichAttribute, String id)
{
NodeList theNodeList = document.getElementsByTagName(tag);
for (int i = 0; i < theNodeList.getLength(); i++) {
try {
String s = (((theNodeList.item(i)).getAttributes()).getNamedItem(whichAttribute)).getNodeValue();
if (s.equals(id)) {
Component temp = (Component)traverse(theNodeList.item(i), null);
temp.setName(id);
return temp;
}
} catch (Exception eh) {
//Ignore exceptions which happen because of blindly calling getNodeName() on null, etc.
}
}
return null;
}
/**
* sets the default Class of something to be klass, provided currentDefaultClass.isAssignableFrom(klass)
* @param currentDefaultClass the class which is currently the default class
* @param klassName the name of the new default class
*/
private Class setDefaultClass(Class currentDefaultClass, Class klass) throws IllegalDefaultClassException {
if (currentDefaultClass.isAssignableFrom(klass)) {
return klass;
} else {
throw new IllegalDefaultClassException("Cannot assign into " + currentDefaultClass + " from " + klass);
}
}
/**
* gets the value of the attribute with the name theAttributeName
* @param theNode the node of the parsed DOM tree whose attributes we are interested in
* @param theAttributeName the name of the attribute whose value we want to return
*/
private String getAttributeValue(Node theNode, String theAttributeName) {
Attr attrs[] = sortAttributes(theNode.getAttributes());
for (int i = 0; i < attrs.length; i++) {
if (attrs[i].getNodeName().equals(theAttributeName)) {
return normalize(attrs[i].getNodeValue());
}
}
return NOT_FOUND;
}
/**
* returns an instance of the default class for the node if a non-default class is not specified
*
* @param node the node that is being processed and inserted into the menu system
* @param anInstanceOfTheDefaultClass an instance of the default class which is kindly provided
* by the caller who knows what class that is
*/
private Object assignGUIObject(Node node, Object anInstanceOfTheDefaultClass) {
String theClassName = getAttributeValue(node, "class");
if (theClassName.equals(NOT_FOUND)) {
return anInstanceOfTheDefaultClass;
} else {
try {
Class klass = Class.forName(theClassName);
if (anInstanceOfTheDefaultClass.getClass().isAssignableFrom(klass)) {
return klass.newInstance();
} else {
System.out.println("ERROR: " + theClassName + " is not a subclass of " + anInstanceOfTheDefaultClass.getClass().getName() + ". Using default class.");
return anInstanceOfTheDefaultClass;
}
} catch (Exception eh) {
System.out.println("ERROR creating an instance of " + theClassName + ". Using default class.");
eh.printStackTrace();
return anInstanceOfTheDefaultClass;//this is the only sensible thing to return
}
}
}
/**
* recursively traverses the subtree with node as the root and creates the corresponding
* hierarchy of GUI objects, returning the topmost object (which, of course, corresponds
* to the root of the tree)
*
* @param node the node that is being processed and inserted into the menu system
* @param realParent the object in the GUI that the node created by processing the node
* which can be referred to as node.getParentNode() (the caller of the method should
* pass in null; this parameter is only used for the recursive construction of the
* corresponding GUI hierarchy)
*/
private Object traverse(Node node, Object realParent) throws BadXMLException, java.lang.IllegalAccessException, java.lang.InstantiationException {
Object theGUIObject = null;
if (node == null) {
return null;
}
int type = node.getNodeType();
switch (type) {
// parse document
case Node.DOCUMENT_NODE: {
traverse(((Document)node).getDocumentElement(), null);
break;
}
// parse element with attributes
case Node.ELEMENT_NODE: {
if(node.getParentNode() == null) {
//this should be a menuSystem
if (node.getNodeName().equals("menuSystem")) {
//we don't do anything here, but we could later
} else {
throw new BadXMLException();
}
} else if (node.getParentNode().getNodeName().equals("menuSystem")) {
if (node.getNodeName().equals("menuBar")) {
theGUIObject = assignGUIObject(node, defaultMenuBarClass.newInstance());
} else if (node.getNodeName().equals("toolBar")) {
theGUIObject = assignGUIObject(node, defaultToolBarClass.newInstance());
((JToolBar)theGUIObject).setOrientation(JToolBar.HORIZONTAL);
} else if (node.getNodeName().equals("popupMenu")) {
theGUIObject = assignGUIObject(node, defaultPopupMenuClass.newInstance());
} else {
throw new BadXMLException("A menuSystem node can only have menuBar and toolBar nodes as children.");
}
} else if (node.getParentNode().getNodeName().equals("menuBar")) {
if (node.getNodeName().equals("menu")) {
String theMenuName = getAttributeValue(node, "name");
if (!(theMenuName.equals(NOT_FOUND))) {
theGUIObject = assignGUIObject(node, defaultMenuClass.newInstance());
((JMenu)theGUIObject).setText(theMenuName);
String theMnemonic = getAttributeValue(node, "mnemonic");
if (!(theMnemonic.equals(NOT_FOUND))) {
//if a mnemonic String is specified set it to the first char
//of the one char string, which *SHOULD* be one character only
((JMenu)theGUIObject).setMnemonic(theMnemonic.charAt(0));
}
((JMenuBar)realParent).add((JMenu)theGUIObject);
}
} else {
throw new BadXMLException("Cannot add a menu with no name attribute.");
}
} else if (node.getParentNode().getNodeName().equals("menu")) {
if (node.getNodeName().equals("menu")) {
//create a submenu
String theMenuName = getAttributeValue(node, "name");
if (!(theMenuName.equals(NOT_FOUND))) {
theGUIObject = assignGUIObject(node, defaultMenuClass.newInstance());
((JMenu)theGUIObject).setText(theMenuName);
String theMnemonic = getAttributeValue(node, "mnemonic");
if (!(theMnemonic.equals(NOT_FOUND))) {
((JMenu)theGUIObject).setMnemonic(theMnemonic.charAt(0));
}
((JMenu)realParent).add((JMenu)theGUIObject);
}
} else if (node.getNodeName().equals("component")) {
//add a menu item
String theMenuItemName = getAttributeValue(node, "name");
if (!(theMenuItemName.equals(NOT_FOUND))) {
String theType = getAttributeValue(node, "type");
if (theType.equals("default")) {
theGUIObject = assignGUIObject(node, defaultMenuItemClass.newInstance());
} else if (theType.equals("radio")) {
theGUIObject = assignGUIObject(node, defaultRadioMenuItemClass.newInstance());
} else if (theType.equals("checkbox")) {
theGUIObject = assignGUIObject(node, defaultCheckboxMenuItemClass.newInstance());
} else {
throw new BadXMLException("Somehow this node doesn't even have the default type.");
}
((JMenuItem)theGUIObject).setText(theMenuItemName);
String theMnemonic = getAttributeValue(node, "mnemonic");
if (!(theMnemonic.equals(NOT_FOUND))) {
((JMenuItem)theGUIObject).setMnemonic(theMnemonic.charAt(0));
}
String theCommand = getAttributeValue(node, "command");
if (!(theCommand.equals(NOT_FOUND))) {
((JMenuItem)theGUIObject).setActionCommand(theCommand);
}
String theMenuItemIconName = getAttributeValue(node, "icon");
if (!(theMenuItemIconName.equals(NOT_FOUND))) {
JASIcon theIcon = (JASIcon)defaultImageIconClass.newInstance();
theIcon.setImage(theMenuItemIconName);
((JMenuItem)theGUIObject).setIcon(theIcon);
}
((JMenu)realParent).add((JMenuItem)theGUIObject);
}
} else if (node.getNodeName().equals("separator")) {
//add a separator
theGUIObject = assignGUIObject(node, defaultMenuSeparatorClass.newInstance());
((JMenu)realParent).add((JSeparator)theGUIObject);
} else {
throw new BadXMLException();
}
} else if (node.getParentNode().getNodeName().equals("popupMenu")) {
if (node.getNodeName().equals("menu")) {
//create a submenu
String theMenuName = getAttributeValue(node, "name");
if (!(theMenuName.equals(NOT_FOUND))) {
theGUIObject = assignGUIObject(node, defaultMenuClass.newInstance());
((JMenu)theGUIObject).setText(theMenuName);
String theMnemonic = getAttributeValue(node, "mnemonic");
if (!(theMnemonic.equals(NOT_FOUND))) {
((JMenu)theGUIObject).setMnemonic(theMnemonic.charAt(0));
}
((JPopupMenu)realParent).add((JMenu)theGUIObject);
}
} else if (node.getNodeName().equals("component")) {
//add a menu item
String theMenuItemName = getAttributeValue(node, "name");
if (!(theMenuItemName.equals(NOT_FOUND))) {
String theType = getAttributeValue(node, "type");
if (theType.equals("default")) {
theGUIObject = assignGUIObject(node, defaultPopupMenuItemClass.newInstance());
} else if (theType.equals("radio")) {
theGUIObject = assignGUIObject(node, defaultPopupRadioMenuItemClass.newInstance());
} else if (theType.equals("checkbox")) {
theGUIObject = assignGUIObject(node, defaultPopupCheckboxMenuItemClass.newInstance());
} else {
throw new BadXMLException("Somehow this node doesn't even have the default type.");
}
((JMenuItem)theGUIObject).setText(theMenuItemName);
String theMnemonic = getAttributeValue(node, "mnemonic");
if (!(theMnemonic.equals(NOT_FOUND))) {
((JMenuItem)theGUIObject).setMnemonic(theMnemonic.charAt(0));
}
String theCommand = getAttributeValue(node, "command");
if (!(theCommand.equals(NOT_FOUND))) {
((JMenuItem)theGUIObject).setActionCommand(theCommand);
}
String theMenuItemIconName = getAttributeValue(node, "icon");
if (!(theMenuItemIconName.equals(NOT_FOUND))) {
JASIcon theIcon = (JASIcon)defaultImageIconClass.newInstance();
theIcon.setImage(theMenuItemIconName);
((JMenuItem)theGUIObject).setIcon(theIcon);
}
((JPopupMenu)realParent).add((JMenuItem)theGUIObject);
}
} else if (node.getNodeName().equals("separator")) {
//add a separator
theGUIObject = assignGUIObject(node, defaultPopupMenuSeparatorClass.newInstance());
((JPopupMenu)realParent).add((JPopupMenu.Separator)theGUIObject);
} else {
throw new BadXMLException();
}
} else if (node.getParentNode().getNodeName().equals("toolBar")) {
if (node.getNodeName().equals("component")) {
//add a toolbar item
String theButtonIconName = getAttributeValue(node, "icon");
if (!(theButtonIconName.equals(NOT_FOUND))) {
theGUIObject = assignGUIObject(node, defaultToolBarItemClass.newInstance());
JASIcon theIcon = (JASIcon)defaultImageIconClass.newInstance();
theIcon.setImage(theButtonIconName);
((JButton)theGUIObject).setIcon(theIcon);
((JButton)theGUIObject).setMargin(new Insets(0,0,0,0));
((JButton)theGUIObject).setActionCommand(getAttributeValue(node, "command"));
((JButton)theGUIObject).setEnabled(false);
((JButton)theGUIObject).setVerticalTextPosition(JButton.BOTTOM);
((JButton)theGUIObject).setHorizontalTextPosition(JButton.CENTER);
((JButton)theGUIObject).setFocusPainted(false);
String theToolTip = getAttributeValue(node, "description");
if (theToolTip.equals(NOT_FOUND)) {
//if there's no description to get the ToolTip from, use the (required) name
theToolTip = getAttributeValue(node, "name");
}
((JButton)theGUIObject).setToolTipText(theToolTip);
//((JButton)theGUIObject).setText(getAttributeValue(node, "name"));
((JToolBar)realParent).add((JButton)theGUIObject);
}
} else if (node.getNodeName().equals("separator")) {
//add a separator
theGUIObject = assignGUIObject(node, defaultToolBarSeparatorClass.newInstance());
((JToolBar)realParent).add((JToolBar.Separator)theGUIObject);
} else {
throw new BadXMLException();
}
} else if (node.getParentNode().getNodeName().equals("component")) {
if (node.getNodeName().equals("accelerator")) {
String theKey = getAttributeValue(node, "key");
String theCtrlMod = getAttributeValue(node, "ctrl");
String theAltMod = getAttributeValue(node, "alt");
String theShiftMod = getAttributeValue(node, "shift");
String theMetaMod = getAttributeValue(node, "meta");
try {
if (!(theKey.equals(NOT_FOUND))) {
((JMenuItem)realParent).setAccelerator(
KeyStroke.getKeyStroke(
java.awt.event.KeyEvent.class.getField(theKey).getInt(null),
( (theCtrlMod.equals("true") ? java.awt.Event.CTRL_MASK : 0)
+ (theAltMod.equals("true") ? java.awt.Event.ALT_MASK : 0)
+ (theShiftMod.equals("true") ? java.awt.Event.SHIFT_MASK : 0)
+ (theMetaMod.equals("true") ? java.awt.Event.META_MASK : 0))));
}
} catch (NoSuchFieldException eh) {
}
}
} else {
System.out.println("menu parsing error: unrecognized parent type: " + node.getParentNode().getNodeName());
}
Attr attrs[] = sortAttributes(node.getAttributes());
for (int i = 0; i < attrs.length; i++) {
Attr attr = attrs[i];
attr.getNodeName();
normalize(attr.getNodeValue());
}
NodeList children = node.getChildNodes();
if (children != null) {
int len = children.getLength();
for (int i = 0; i < len; i++) {
traverse(children.item(i), theGUIObject);
}
}
break;
}
// handle entity reference nodes
case Node.ENTITY_REFERENCE_NODE: {
node.getNodeName();
break;
}
// parse text
case Node.CDATA_SECTION_NODE: {
}
case Node.TEXT_NODE: {
normalize(node.getNodeValue());
break;
}
// parse processing instruction
case Node.PROCESSING_INSTRUCTION_NODE: {
node.getNodeName();
String data = node.getNodeValue();
break;
}
}
if (type == Node.ELEMENT_NODE) {
node.getNodeName();
}
return theGUIObject;
}
/** Normalizes the given string. */
private String normalize(String s) {
StringBuffer str = new StringBuffer();
int len = (s != null) ? s.length() : 0;
for (int i = 0; i < len; i++) {
char ch = s.charAt(i);
switch (ch) {
case '<': {
str.append("<");
break;
}
case '>': {
str.append(">");
break;
}
case '&': {
str.append("&");
break;
}
case '"': {
str.append(""");
break;
}
case '\r':
case '\n':
default: {
str.append(ch);
}
}
}
return str.toString();
} // normalize(String):String
/** Returns a sorted list of attributes. */
private Attr[] sortAttributes(NamedNodeMap attrs) {
int len = (attrs != null) ? attrs.getLength() : 0;
Attr array[] = new Attr[len];
for (int i = 0; i < len; i++) {
array[i] = (Attr)attrs.item(i);
}
for (int i = 0; i < len - 1; i++) {
String name = array[i].getNodeName();
int index = i;
for (int j = i + 1; j < len; j++) {
String curName = array[j].getNodeName();
if (curName.compareTo(name) < 0) {
name = curName;
index = j;
}
}
if (index != i) {
Attr temp = array[i];
array[i] = array[index];
array[index] = temp;
}
}
return array;
} // sortAttributes(NamedNodeMap):Attr[]
//
// private variables
//
private static String xmlParser;
private static final String NOT_FOUND = "ATTRIBUTE NOT FOUND";
private static final String DEFAULT_PARSER_NAME = "jas2.util.xmlmenus.parserwrappers.IBMDOMParser";
private static Document document;//org.w3c.dom.Document
private static Class defaultMenuBarClass;
private static Class defaultMenuClass;
private static Class defaultMenuItemClass;
private static Class defaultRadioMenuItemClass;
private static Class defaultCheckboxMenuItemClass;
private static Class defaultMenuSeparatorClass;
private static Class defaultToolBarPanelClass;
private static Class defaultToolBarClass;
private static Class defaultToolBarItemClass;
private static Class defaultToolBarSeparatorClass;
private static Class defaultImageIconClass;
private static Class defaultPopupMenuClass;
private static Class defaultPopupMenuSeparatorClass;
//Sun doesn't have these next 3 yet but (in case they ever do) we'll add them in now
private static Class defaultPopupMenuItemClass;
private static Class defaultPopupRadioMenuItemClass;
private static Class defaultPopupCheckboxMenuItemClass;
}
Ads help maintain this website.