I do a lot XML and Xslt processing at my workplace. Altova XmlSpy has XPath tool for evaluating queries on edited XML file. I'm missing that (useful) feature in Netbeans so I decided to build something similar. Of course it's far away from Altova's version in features but it can be useful (and you can always extend it).
- New project: Choose File > New Project (Ctrl-Shift-N). Under Categories, select NetBeans Modules. Under projects, select Module and click Next.
- In the Name and Location panel, type XPathEvaluator in Project Name. Change the Project Location to any directory on your computer, such as /home/damir/Projects/netbeans. Leave the Standalone Module radio button selected.
- In the Basic Module Configuration panel, replace yourorghere in Code Name Base with netbeans.modules, so that the whole code name base is org.netbeans.modules.xpathevaluator. Leave the location of the localizing bundle and XML layer, so that they will be stored in a package with the name org/netbeans/modules/xpathevaluator.
- Click finish.
In project tree right click on Libraries Node > Add module dependency...
-
Import Datasystems API by typing datasystems
It contains XmlDataObject which we will listen for.
- New file (Ctrl-N). Under Categories, select Module Development. Under file types, select Window Component and click Next.
- Window Position: output
- Leave Open on Application start unchecked
- Class Name Prefix: XPathEvaluator
- Package: org.netbeans.modules.xpathevaluator
- Click finish
- 2x JLabel
- 1x JTextField for XPath input
- 1x JTextArea for XPath results
- New file (Ctrl-N). Under Categories, select Module Development. Under file types, select Action and click Next.
- Check conditionally enabled and choose EditorCookie for cookie class
- GUI registration:
- Category: XML
- Uncheck Global Menu Item
- Check Editor Context Menu and choose text/xml for content type, change position to Tools - HERE
- Check Separator before and Separator after
- Name, Icon, and Location:
- Class Name: ShowXPathEvaluatorAction
- Display Name: XPath Evaluator
- Package Name: org.netbeans.modules.xpathevaluator
- Click finish
PerformAction method should look like this:
protected void performAction(Node[] activatedNodes) { EditorCookie editorCookie = activatedNodes[0].getLookup().lookup(EditorCookie.class); try { DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); domFactory.setNamespaceAware(true); DocumentBuilder builder = domFactory.newDocumentBuilder(); Document doc = builder.parse(((org.openide.text.CloneableEditorSupport) editorCookie).getInputStream()); XPathEvaluatorTopComponent win = XPathEvaluatorTopComponent.findInstance(); win.setDocument(doc); win.open(); win.requestActive(); } catch (SAXException ex) { //Exceptions.printStackTrace(ex); } catch (ParserConfigurationException ex) { //Exceptions.printStackTrace(ex); } catch (IOException ex) { //Exceptions.printStackTrace(ex); } }Press Ctrl+I to fix imports. Make sure you select org.w3c.dom.Document from combo box.
We have to:
- create private member which will hold org.w3c.dom.Document from selected editor and on which we will execute XPath.
- create setDocument(org.w3c.dom.Document doc) method for setting current xml document.
- create evaluate() method for executing and displaying XPath query.
- add DocumentListener to jTextField1 for executing XPath query as we type.
- listen for changes in TopComponent selection (when user opens/activates/closes other xml or non xml panel).
This method is simple. It evaluates text from jTextField1 control and fills jTextArea1 control with results after clearing the same. If there were errors it shows them at bottom of window (jLabel2). xmlDoc is current xml document.
private void evaluate() { if (xmlDoc != null) { try { XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); XPathExpression expr = xpath.compile(jTextField1.getText()); NodeList nodes = (NodeList) expr.evaluate(xmlDoc, XPathConstants.NODESET); jTextArea1.setText(""); for (int i = 0; i < nodes.getLength(); i++) { jTextArea1.setText(jTextArea1.getText() + nodes.item(i).getNodeValue() + "\n"); } } catch (XPathExpressionException ex) { jLabel2.setText(ex.getMessage()); } } }
Append following code at the end of XmlPathEvaluatorTopComponent's constructor:
jTextField1.getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { evaluate(); } public void removeUpdate(DocumentEvent e) { evaluate(); } public void changedUpdate(DocumentEvent e) { } });
Following code is based on Netbeans Selection Managment Tutorial I. We have to listen for global selection changes of "Object". In our case it is XmlDataObject. We have to extend our TopComponent to implement LookupListener.
final class XPathEvaluatorTopComponent extends TopComponent implements LookupListenerLookupListener provides a method for handling changes (resultChanged(LookupEvent ev)). We have to check if there are any instances of XmlDataObject selected and change xmlDoc value according to that.
- If there are any XmlDataObject instances (in this case only one) then we have set xmlDoc member's value to selected XmlDataObject's XmlDocument and we have to enable input in window.
- If there are not any instances then we have to set it to null and to disable input on window.
To avoid this we have to check if our window (XPathEvaluator) is selected.
public void resultChanged(LookupEvent arg0) { // if this window selected then do nothing if (TopComponent.getRegistry().getActivated().equals(this)) { // do nothing } else { Lookup.Result r = (Lookup.Result) arg0.getSource(); Collection c = r.allInstances(); // if there are instances if (!c.isEmpty()) { try { XMLDataObject o = (XMLDataObject) c.iterator().next(); setDocument(o.getDocument()); evaluate(); jLabel2.setText(o.getName()); jTextArea1.setEnabled(true); jTextField1.setEnabled(true); } catch (IOException ex) { jLabel2.setText(ex.getMessage()); } catch (SAXException ex) { jLabel2.setText(ex.getMessage()); } } else { xmlDoc = null; jTextArea1.setEnabled(false); jTextField1.setEnabled(false); jLabel2.setText("no selection"); } } }Now we have to register LookupListener. We will do that when window is opened:
@Override public void componentOpened() { Lookup.Template tpl = new Lookup.Template(org.openide.loaders.XMLDataObject.class); result = Utilities.actionsGlobalContext().lookup(tpl); result.addLookupListener(this); }When we close window we have to unregister the listener:
@Override public void componentClosed() { result.removeLookupListener(this); result = null; }We have to create a member for holding the result where lookup listener is registered. You can find explanation for this in Netbeans Selection Managment Tutorial I.
8 comments:
Thanks! You make really nice tutorials.
Thank you for reading and I'm glad you like them :)
Hi,
My name is Varun Nischal and I'm the NetBeans Community Docs Contribution Coordinator. Your blog entry would make a fantastic tutorial for our Community Docs wiki (http://wiki.netbeans.org/CommunityDocs).
Would you be willing to contribute it? If you need any help or have any questions, please contact me at nvarun@netbeans.org
I look forward to hearing from you.
Thanks,
Varun Nischal
http://nb-community-docs.blogspot.com/
--
"You must do the things you think you cannot do."
You should make this plugin available for download. It would be very helpful.
Hi Al,
you can find downloadable source code in this post.
All the best,
Damir
Any chance of releasing this as a plugin? If not do you know if code works with latest version of netbeans 6.7 M2
Well, I didn't planned releasing it as plugin because it's not production ready (in my opinion). You can download source code and see if it works in Netbeans 6.7 M2.
This really looks like something that should be part of NB plugins. Exactly what I need right now but unfortunately don't have time to install it from source.
Looks great. I hope to see this released soon!
Post a Comment