Java – vytvoření Swing menu z XML souboru

Swing menu builder

Téměř každá aplikace, která má GUI, obsahuje také nějaké to menu. Vytvořit takové menu není nic těžkého. Pokud ale vyvíjíte aplikaci, u které ještě zdaleka není jisté, jak bude výsledné menu vypadat, je docela dobrý nápad toto menu tvořit dynamicky – za pomocí nějakých metadat. V tomto článku vám ukážu jedno z možných řešení – vytvoření Swingovského menu, které bude definováno v XML souboru (jak jinak u mně 😉 ). Pojdmě na to.

Nejdříve si ujasníme, co vlastně budeme dělat. Vytvoříme si XML soubor, který bude obsahovat něco takového:

<menubar>
  <menu name="file">
    <item name="new">
      <command name="new"/>
    </item>
    <item name="save">
      <command name="save"/>
    </item>
    <item name="load">
      <command name="load"/>
    </item>
  </menu>
  <menu name="edit">
    <item name="copy">
      <command name="copy"/>
    </item>
    <item name="cut">
      <command name="cut"/>
    </item>
    <item name="paste">
      <command name="paste"/>
    </item>
  </menu>
</menubar>

Jak vidíte, struktura tohoto souboru je velmi jednoduchá. Pro zjednodušení budeme počítat pouze s jednou úrovní menu. To znamená, že nebudeme brát v potaz možnost, že některá položka v menu obsahuje další podmenu.

Nejdříve si vytvoříme základní třídu pro zpracovávaní XML souborů. Její hlavní funkce je parsování souboru a vrácení dokumentu. Ke zpracování XML používám nejraději DOM, který je pro tyto malé soubory ideální. Celý obsah souboru načte do paměti (včetně struktury) a vy se v něm pak můžete bez problému pohybovat jako v klasickém stromu.

Třída XmlBase bude vypadat takto:

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public abstract class XmlBase {
	private Document document;
	private DocumentBuilderFactory dbf;
	private DocumentBuilder builder;
	DOMImplementation domImplementation;
	private String rootElement;
	
	
	public XmlBase(String fileName, String rootElement) {
		this.rootElement = rootElement;
		dbf = DocumentBuilderFactory.newInstance();
		try {
			builder = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		domImplementation = builder.getDOMImplementation();
		
		File xmlFile = new File(fileName);
		if (xmlFile.exists()) {
			try {
				document = builder.parse(xmlFile);
			} catch (SAXException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			document = createDocument();
		}
	}
	
	protected Document createDocument() {
		Document tempDoc = domImplementation.createDocument(null, rootElement, null);
		tempDoc.createElement(rootElement);
		return tempDoc;
	}
	
	protected Document getDocument() {
		return document;
	}

}

Tato třída obstará získání objektu typu Document, se kterým budeme poté pracovat. Pro převedení XML elementů na jednotlivé položky menu vytvoříme třídu XmlMenuBuilder, která rozšíří třídu XmlBase. Tato třída už konečně bude mít na starosti vytváření menu. Třída bude definována takto:

import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XmlMenuBuilder extends XmlBase {
	
	private static final String ROOT_ELEMENT = "menubar";
	
	protected Document document;


	
	public XmlMenuBuilder(String fileName) {
		super(fileName, ROOT_ELEMENT);
		document = super.getDocument();
	}

	public JMenuBar getMenuBar() {
		document = super.getDocument();
		JMenuBar menuBar = new JMenuBar();
		NodeList menus = document.getElementsByTagName("menu");
		for (int i = 0; i < menus.getLength(); i++) {
			JMenu menu = new JMenu(getName(menus.item(i)));
			NodeList menuItems = menus.item(i).getChildNodes();
			for (int ii = 0; ii < menuItems.getLength(); ii++) {
				if (menuItems.item(ii).getNodeName().equals("item")) {
					JMenuItem item = new JMenuItem(getName(menuItems.item(ii)));
					item.setActionCommand(getMenuItemCommand(menuItems.item(ii)));
					menu.add(item);
				}
			}
			menuBar.add(menu);
		}
		assert(menuBar != null);
		return menuBar;
	}

	private String getName(Node node) {
		return node.getAttributes().getNamedItem("name").getNodeValue();
	}
	
	private String getMenuItemCommand(Node node) {
		NodeList nl = node.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			if (nl.item(i).getNodeName().equals("command")) {
				String command = nl.item(i).getAttributes().getNamedItem("name").getNodeValue();
				if (!command.equals(""))
					return command;
			}
		}
		return "";
	}
}

&#91;/sourcecode&#93;
</p>
</p><p>Tak a to je celé. Vytvořený kód můžeme otestovat touto testovací třídou:

import javax.swing.JFrame;
import javax.swing.JMenuBar;

public class SwingMenuBuilderExample {
	
	public static void main(String[] args) {
		JFrame mainFrame = new JFrame("Menu builder example");
		XmlMenuBuilder menuBuilder = new XmlMenuBuilder("menu.xml");
		JMenuBar menuBar = menuBuilder.getMenuBar();
		mainFrame.setJMenuBar(menuBar);
		
		mainFrame.setSize(640, 480);
		mainFrame.setVisible(true);
	}

}

Proč používat XML?

Pokud jste dočetli až sem, možná se divíte, proč to dělat tak složitě, když stačilo menu vytvořit někde přímo v kódu. Jasně, určitě by to bylo rychlejší, ale v momentě, kdy byste potřebovali menu upravit, museli byste hrabat v kódu(což přináší nebezpečí zanesení nových chyb) a všechno znovu kompilovat. Při vytváření menu z XML souboru (nebo jakého koliv jiného textového souboru) vám tyto starosti odpadají. Předělání menu bude poté otázkou okamžiku a riziko zanesení chyb bude malé.

Nakonec bych ještě dodal, že přiložené kusy kódu nejsou úplně dokonalé. Určitě bych doporučil místo přímého používání XmlMenuBuilder třídy používat nějaké rozhraní (vždy programujte proti rozhraní!), které bude XmlMenuBuilder implementovat. Díky tomu pak můžete vyměnit lehce způsob, jakým bude menu vytvořeno. Pokud v kódu najdete nějakou chybu, dejte mi prosím vědět v komentářích. Tento kód jsem sice otestoval, ale je možné, že mi nějaká chybka unikla. Nakonec přikládám zabalený Eclipse projekt, který můžete jednoduše v Eclipsu naimportovat. Projekt stahujte ZDE.

Příspěvek byl publikován v rubrice Programování. Můžete si uložit jeho odkaz mezi své oblíbené záložky.

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *