package net.java.xpath.ui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.border.BevelBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import org.netbeans.api.editor.EditorRegistry;
import org.openide.awt.Mnemonics;
import org.openide.cookies.EditCookie;
import org.openide.cookies.OpenCookie;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.text.CloneableEditorSupport;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
/**
* Top component for XPath evaluation.
* @author Michael Bien
*/
public final class XPathTopComponent extends TopComponent {
private static XPathTopComponent instance;
/** path to the icon used by the component and its open action */
public static final String ICON_PATH = "net/java/xpath/ui/utilities-terminal.png";
private static final String PREFERRED_ID = "XPathTopComponent";
private XPathEvaluatorThread evaluator;
public JTextComponent lastFocusedEditor;
private XPathTopComponent() {
// org.netbeans.spi.editor.mimelookup.MimeDataProvider mdp = Lookup.getDefault().lookup(MimeDataProvider.class);
// Lookup mime = mdp.getLookup(MimePath.get("text/x-xpath"));
// java.util.prefs.Preferences pref = mime.lookup(Preferences.class);
// pref.putInt("SimpleValueNames.TEXT_LIMIT_WIDTH"/*org.netbeans.modules.editor.lib.EditorPreferencesKeys.TEXT_LIMIT_WIDTH*/, 1);
initComponents();
setName(NbBundle.getMessage(XPathTopComponent.class, "CTL_XPathTopComponent"));
setToolTipText(NbBundle.getMessage(XPathTopComponent.class, "HINT_XPathTopComponent"));
setIcon(ImageUtilities.loadImage(ICON_PATH, true));
outputPane.setEditorKit(CloneableEditorSupport.getEditorKit("text/xml"));
xpathTextField.setEditorKit(CloneableEditorSupport.getEditorKit("text/x-xpath"));
xpathTextField.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
xpathTextFieldChanged(e);
}
public void removeUpdate(DocumentEvent e) {
xpathTextFieldChanged(e);
}
public void changedUpdate(DocumentEvent e) {
xpathTextFieldChanged(e);
}
});
// hack: consume the return key to prevent new lines
xpathTextField.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER)
e.consume();
}
public void keyTyped(KeyEvent e) { }
public void keyReleased(KeyEvent e) { }
});
EditorRegistry.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if(EditorRegistry.FOCUS_GAINED_PROPERTY.equals(evt.getPropertyName())) {
if(xpathTextField != evt.getOldValue() && evt.getOldValue() instanceof JTextComponent) {
lastFocusedEditor = (JTextComponent)evt.getOldValue();
}
}
}
});
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
JLabel expressionLabel = new JLabel();
JScrollPane scrollPane = new JScrollPane();
outputPane = new JEditorPane();
JButton saveButton = new JButton();
xpathTextField = new JEditorPane();
Mnemonics.setLocalizedText(expressionLabel, NbBundle.getMessage(XPathTopComponent.class, "XPathTopComponent.expressionLabel.text")); // NOI18N
outputPane.setEditable(false);
outputPane.setFocusable(false);
scrollPane.setViewportView(outputPane);
Mnemonics.setLocalizedText(saveButton, NbBundle.getMessage(XPathTopComponent.class, "XPathTopComponent.saveButton.text"));
saveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
saveButtonActionPerformed(evt);
}
});
xpathTextField.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
xpathTextField.setFont(new JTextField().getFont());
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(Alignment.TRAILING)
.addComponent(scrollPane, Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(expressionLabel)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(xpathTextField)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(saveButton)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createParallelGroup(Alignment.BASELINE)
.addComponent(expressionLabel)
.addComponent(saveButton))
.addComponent(xpathTextField, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 264, Short.MAX_VALUE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void saveButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_saveButtonActionPerformed
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Export As...");
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.setDialogType(JFileChooser.SAVE_DIALOG);
chooser.setFileFilter(new FileNameExtensionFilter("XML files", "xml", "xsd", "xls", "html"));
if (JFileChooser.APPROVE_OPTION == chooser.showSaveDialog(this)) {
File output = chooser.getSelectedFile();
if(output != null) {
String content = outputPane.getText();
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(output);
fileWriter.write(content);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} finally {
if(fileWriter != null) {
try {
fileWriter.close();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
try{
DataObject dao = DataObject.find(FileUtil.toFileObject(output));
OpenCookie oc = dao.getLookup().lookup(OpenCookie.class);
if(oc != null) {
oc.open();
}else{
EditCookie ec = dao.getLookup().lookup(EditCookie.class);
if(ec != null) {
ec.edit();
}
}
}catch(Exception ex) {
Logger.getLogger(this.getClass().getName()).log(
Level.WARNING, "could not open exported xml file in editor", ex);
}
}
}
}//GEN-LAST:event_saveButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private JEditorPane outputPane;
private JEditorPane xpathTextField;
// End of variables declaration//GEN-END:variables
private final void xpathTextFieldChanged(DocumentEvent e) {
try {
String text = e.getDocument().getText(0, e.getDocument().getLength());
JTextComponent lastFocusedComponent = EditorRegistry.lastFocusedComponent();
if(lastFocusedComponent != xpathTextField)
lastFocusedEditor = lastFocusedComponent;
if(lastFocusedEditor != null) {
String editorContent = lastFocusedEditor.getText();
if(editorContent != null) {
evaluator.asyncEval(text, editorContent);
}else {
outputPane.setText("<please focus an edior containing a xml file first>");
}
}
} catch (BadLocationException ex) {
// we can ignore this;
}
}
/**
* Gets default instance. Do not use directly: reserved for *.settings files only,
* i.e. deserialization routines; otherwise you could get a non-deserialized instance.
* To obtain the singleton instance, use {@link #findInstance}.
*/
public static synchronized XPathTopComponent getDefault() {
if (instance == null) {
instance = new XPathTopComponent();
}
return instance;
}
/**
* Obtain the XPathTopComponent instance. Never call {@link #getDefault} directly!
*/
public static synchronized XPathTopComponent findInstance() {
TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
if (win == null) {
Logger.getLogger(XPathTopComponent.class.getName()).warning(
"Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system.");
return getDefault();
}
if (win instanceof XPathTopComponent) {
return (XPathTopComponent) win;
}
Logger.getLogger(XPathTopComponent.class.getName()).warning(
"There seem to be multiple components with the '" + PREFERRED_ID +
"' ID. That is a potential source of errors and unexpected behavior.");
return getDefault();
}
@Override
public int getPersistenceType() {
return TopComponent.PERSISTENCE_ALWAYS;
}
@Override
public void componentOpened() {
if(evaluator == null) {
evaluator = new XPathEvaluatorThread(outputPane);
}
}
@Override
public void componentClosed() {
if(evaluator != null) {
evaluator.interrupt();
evaluator = null;
}
}
/** replaces this in object stream */
@Override
public Object writeReplace() {
return new ResolvableHelper();
}
@Override
protected String preferredID() {
return PREFERRED_ID;
}
final static class ResolvableHelper implements Serializable {
private static final long serialVersionUID = 1L;
public Object readResolve() {
return XPathTopComponent.getDefault();
}
}
}