Accessing XML through IDEA DOM and UI problems

I've been following the article at http://www.jetbrains.com/idea/documentation/dom-rp.html to create a custom XML editor plugin.

Is there an example anywhere for how this all works? At the moment I'm getting the following exception

java.lang.ClassCastException: javax.swing.JTextField cannot be cast to com.intellij.util.xml.ui.TextPanel
at com.intellij.util.xml.ui.TextControl.createMainComponent(TextControl.java:40)
at com.intellij.util.xml.ui.EditorTextFieldControl.createMainComponent(EditorTextFieldControl.java:97)
at com.intellij.util.xml.ui.BaseControl.initialize(BaseControl.java:86)
at com.intellij.util.xml.ui.BaseControl.bind(BaseControl.java:115)
at com.intellij.util.xml.ui.BasicDomElementComponent.doBind(BasicDomElementComponent.java:103)
at com.intellij.util.xml.ui.BasicDomElementComponent.bindProperties(BasicDomElementComponent.java:86)
at com.intellij.util.xml.ui.BasicDomElementComponent.bindProperties(BasicDomElementComponent.java:54)
at org.chrismckay.tools.reportDefiner.ui.DecoratorEditorWindow.<init>(DecoratorEditorWindow.java:23)



I appear to be accessing the DOM OK (after replacing the deprecated methods in the article).

I have a FileDescription configured in the plugin.xml

      <dom.fileDescription implementation="org.chrismckay.tools.reportDefiner.dom.ReportDomFileDescription"/>
      <fileEditorProvider id="Report Decorator Editor" implementation="org.chrismckay.tools.reportDefiner.editor.DecoratorEditorProvider"></fileEditorProvider>


public class ReportDomFileDescription extends DomFileDescription<Report> {
    private static final String ROOT_TAG_NAME = "reportDefinition";

    public ReportDomFileDescription() {
        super(Report.class, ROOT_TAG_NAME);
    }

}

and interfaces defined

public interface Report extends DomElement {
    SystemName getSystemName();
    Decoration getDecoration();
}


public interface Decoration extends DomElement {
    GenericDomValue<String> getSystemName();
}


the XML file looks like this

<?xml version="1.0" encoding="UTF-8"?>
<reportDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../report.xsd">
    <decoration>
           <systemName>A System Name</systemName>
    </decoration>
</reportDefinition>


My EditorProvider looks like this:

public class DecoratorEditorProvider implements FileEditorProvider, DumbAware {
    @Override
    public boolean accept(@NotNull Project project, @NotNull VirtualFile virtualFile) {
        PsiManager psiMgr = PsiManager.getInstance(project);
        PsiFile psiFile = psiMgr.findFile(virtualFile);
        if (psiFile == null || !(psiFile instanceof XmlFile)) {
            return false;
        }
        DomManager manager = DomManager.getDomManager(project);

        // if the XML file has a root of "reportDefinition" use this editor
        return !(manager.getFileElement((XmlFile) psiFile, Report.class) == null);
    }

    @NotNull
    @Override
    public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile virtualFile) {
        PsiManager psiMgr = PsiManager.getInstance(project);
        PsiFile psiFile = psiMgr.findFile(virtualFile);
        if (psiFile == null || !(psiFile instanceof XmlFile)) {
            return null;
        }
        DomManager manager = DomManager.getDomManager(project);

        Decoration decoration = manager.getFileElement((XmlFile) psiFile, Report.class).getRootElement().getDecoration();

        DomFileEditor domFileEditor = new DomFileEditor(decoration, "Decoration", new DecoratorEditorWindow(decoration));

        return domFileEditor;
    }



I am not inheriting from PerspectiveFileEditorProvider as suggested in the article as I could not see an accept method, which I want so as to bring up the editor only for certain xml files

The UI form class looks like this:

public class DecoratorEditorWindow extends BasicDomElementComponent<Decoration> {
    private JPanel reportEditorWindow;
    private JTextField mySystemName;

    public DecoratorEditorWindow(final Decoration domElement) {

        super(domElement);

         // exception thrown here
        bindProperties();
    }

    public JPanel getParentWindow() {
        return reportEditorWindow;
    }

    @Override
    public JComponent getComponent() {
        return reportEditorWindow;
    }


Any thoughts or pointers to any examples? I've searched for any occurences of DomFileEditor in the community source code, but no examples as such of it's usage.


Thanks,
Chris


18 comments
Comment actions Permalink

Chris,

The article is pretty much outdated, sorry for that. You should always bind TextPanel controls to GenericDomValue's, not JTextField.

0
Comment actions Permalink

For anyone who stumbles across this thread. Note that the Interface files MUST have the appropriate annotations for values to be returned (at least when developing for IntelliJ 11)

The examples shown are a bit messed up. For the XML file shown, the interfaces should be

public interface Report extends DomElement {
    @SubTag("decoration")
    Decoration getDecoration();
}


public interface Decoration extends DomElement {
    @SubTag("systemName")
    SystemName getSystemName();
}


public interface SystemName extends DomElement {
    @TagValue
    String getValue();
}
0
Comment actions Permalink

Thanks for the reply, but isn't there an example anywhere? The functionality provided by DomUIControl is exactly what I want in my plugin. It seems that others have found an example to follow in the Spring plugin, but it seems that this source has disappeared. I have searched though all the IntelliJ 11 CE source, plus a large number of plugins of varying quality.

I've taken the example back to as simple a case as I can find.

I get a GenericDomValue from the XMLFile

I create a DomUIControl TextControl

Then surely I need to attach the control to a Swing component created with the IntelliJ GUI designer, to get the text field to display and accept input

private TextControl uiSystemName;
private JTextField mySystemName;
...

            GenericDomValue<String> s = systemName.getValue();

            uiSystemName = new TextControl(new DomStringWrapper(s));

            uiSystemName.bind(mySystemName);

This produces the same error that I reported originally.
0
Comment actions Permalink

any chance this get updated. I was also having problems getting this working (based on old article).
Actually, whole plugin development section should have some more attention e.g. API changes few more examples wouldn't hurt. I think for someone from Jetbarins it will not take to much time to update current pages and it wouls save plugin developers a lot of time.
Right now, only way to find out how things work is digging through sources which takes to much time.

0
Comment actions Permalink

Don't create TextControl by yourself. You add a TextPanel in the UI designer, name it after the GenericDomValue property you want to edit, call bindProperties(); from your constructor (assuming that you extend BasicDomElementComponent) and let it do all the binding. JTextField will be created for you automatically during this process.

Unfortunately only JavaEE, JSF and some app server integrations are using this API and they're all closed source, so there are effectively no examples available. Please ask here if you have any further questions.

0
Comment actions Permalink

Some more success now. I have a GUI form editor coming up with the correct value in a text field. Once I realized that I had to manually add the TextControl to the UIDesigner palette things became a lot clearer....

I have a few issues left.

Firstly the TextControl component cannot be resized from within the UI Designer. Grabbing the window handles does not work, nor does setting minimum size values from (-1, -1). I'll try to override these values in a custom component creation method, but I am worried this may break my binding.

Secondly I tried to add BooleanControl and ComboControl to the palette but was unsuccessful with the error "Please select a class derived from JComponent" when I selected com.intellij.util.xml.ui.ComboControl" as the Class. I particularly want to use a combo box in my editor.

Lastly, when I close IntelliJ after running my plugin I see this Assertion failed message:

[  34821]  ERROR - ChangeSignatureGestureDetector - Assertion failed: {LightVirtualFile: /a.txt=com.intellij.refactoring.changeSignature.ChangeSignatureGestureDetector$MyDocumentChangeAdapter@3f968ab9}
java.lang.Throwable
 at com.intellij.openapi.diagnostic.Logger.assertTrue(Logger.java:98)
 at com.intellij.refactoring.changeSignature.ChangeSignatureGestureDetector$1.dispose(ChangeSignatureGestureDetector.java:139)
 at com.intellij.openapi.util.Disposer$1.execute(Disposer.java:46)
 at com.intellij.openapi.util.Disposer$1.execute(Disposer.java:44)



I'm going to ignore this for now...

If I can get the text field sizing correctly I'll upload my sample application (I've reworked it so it follows the xml example on the outdated How-to page) so that others can get a helping hand.

C
0
Comment actions Permalink

Again, it's TextPanel that you should work with in UI designer, not TextControl. It's an (almost) normal JPanel and allows all kinds of customization: size policies, min/max/pref sizes.

For editing GenericDomValue<Boolean> and GenericDomValue<Enum> use JCheckBox and JComboBox as the UI counterparts. Most probably you don't need to create BooleanControl and ComboControl directly.

Yes, it's a great idea to upload some code so that people actually have some example on how to use this. In addition, if I see the full code, I could perhaps help better.

0
Comment actions Permalink

A bit of stupidity in the previous post - BooleanControls and ComboControls are just bound to the usual Swing suspects.

Having said that, trying to bind a list of items to the Swing component and the Dom value is proving hard to implement. I feel like I'm feeling my way round a darkened room trying to get this going.

I'm going to attach my entire project (the simplified one trying to replicate the original document) to this post, which may prove useful to other following in my footsteps.

Unless I can get some more help from JetBrains I think I'll have to forget all about trying to get this working. I would suggest that JetBrains remove the obsolete document from their web site to prevent others wasting their time.

I think I'll just do a clunky Swing application with change listeners everywhere...

C



Attachment(s):
Archive.zip
0
Comment actions Permalink

Sorry Peter, didn't see this message until just now. I now have Post-It note on the monitor re-reminding me that Controls are not JComponents!

However, apart from anything else (as per my latest post) you still can't size the TextPanel in the UI Designer. I'll have a little play with doing it in code.

C

0
Comment actions Permalink

thx, that was helpful. Any idea how to deal with xml  with (multiple) namespaces? e.g.
<foo:root xmlns:foo...xmlns:bar... >
<foo:something>
<bar:something>
</foo:root>

thx..

0
Comment actions Permalink

Interesting. Can you do it with a usual JPanel?

0
Comment actions Permalink

That's not covered in the article but there are some javadocs for it. You should create different DOM interfaces for foo:something and bar:something and annotate them using @Namespace annotation passing different strings (namespace keys) there. Then, in your file description, override initializeFileDescription method and call registerNamespacePolicy from there passing a mapping from namespace key to the actual namespace. Most probably this will look like:

  protected void initializeFileDescription() {
    registerNamespacePolicy(FOO_NAMESPACE_KEY, FOO_NAMESPACE);
    registerNamespacePolicy(BAR_NAMESPACE_KEY, BAR_NAMESPACE);
  }


If your case is very simple and the namespaces are always static, then of course you can also make keys the same as namespaces, but you'll need to register the mapping anyway.
0
Comment actions Permalink

Yep, no problem there...

Screen shot 2012-01-20 at 2.59.31 PM.png

0
Comment actions Permalink

What are the relevant properties in inspector? I mean horizontal/vertical size policies, pref/min/max sizes, etc.?

0
Comment actions Permalink

Peter,

Here's more information

Environment:
IntelliJ 11 CE 11.0.1
Build #IC-111.167
JDK 1.6.0_29 64bit server edition

MacBook Pro OS X 10.6.8


Steps within a Project:
New GUI Form

Form name: TestTextPanel
Base layout manager: GridLayoutManager (IntelliJ)
Create bound class: ON

Drag TextPanel to Form
Horizontal Size Policy: Can Shrink, Can Grow
Vertical Size Policy: Can Shrink, Can Grow
Minimum Size: [-1, -1]
Preferred Size: [-1,-1]
Maximum Size: [-1,-1]

Grab any resize handle on TextPanel to stretch, no effect

Manually Change Minimum Size to [150,-1] - no effect on TextPanel in UI
Manually Change Preferred Size to [150,-1] - no effect on Text Panel in UI

Manually Change Minimum Size to [150,1] - no effect on TextPanel in UI
Manually Change Preferred Size to [150,1] - no effect on Text Panel in UI

Run preview, Entry Field is 1 char high, 0 chars wide (as per previous screen shot)

Added JPanel and JTextField within it to Form
JPanel sizes to form width
Horizontal Size Policy: Can Shrink, Can Grow
Vertical Size Policy: Can Shrink, Can Grow
Minimum Size: [-1, -1]
Preferred Size: [-1,-1]
Maximum Size: [-1,-1]

JTextField sizes to panel width
Horizontal Size Policy: Can Grow, Want Grow
Vertical Size Policy: Fixed
Minimum Size: [-1, -1]
Preferred Size: [150,-1]
Maximum Size: [-1,-1]

Run preview, JPanel and JTextField display as displayed in UI. This time TextPanel is displayed at 1 char high and about 70% of JTextField width, centered in Form

In UI Designer TextPanel is 1 char high, 0 chars wide.

I've attached the .form

Let me know if there is anything else I can provide. Surely it's not a Mac issue?


Any thoughts on how to get the ComboBox example to work?



C

Attachment(s):
TestTextPanel.form.zip
0
Comment actions Permalink

I've got a workaround, if I use the FormLayout as my LayoutManager, as opposed to the GridLayoutManager, the TextPanel sizes to the width of the Form JPanel. That will suit me for now.

Screen shot 2012-01-21 at 10.31.56 AM.png
For all my usual Swing form stuff, I've always used the default GridLayoutManager, but for this plugin FormLayout will probably be fine.

Let me know if you want me to do further any investigation.

OK, now - how to get the ComboBox going!

C

0
Comment actions Permalink

More success!

I have successfully bound TextControls and ComboControls to Dom elements. I had missed placing a Control.reset() call after the bind.

Still a few issues such as the value of the selected combo list not updating the Dom element (missing some sort of commit?) and a problem of when the Project is first opened, the xml file that was already opened does not show the Editor tabs.

I shall do some more digging when back at work

C

0
Comment actions Permalink

OK, we can call this thread answered I think.

Attached to this post is the final example plugin project. The plugin associates a custom editor to XML files that contain the root tag "xmlExample". When a file of that type is opened, a window is opened with three tabs.

"Text" which opens a default XML text editor

"XML Form Editor" which opens a GUI form with a text entry field and combo box showing some of the contents of an opened xml file in the GUI fields. Also shown is a checkbox - this is has no functionality at present

There is another tab "Another XML Form Editor" which shows another value from the XML file. This editor uses a method (bindProperties) which is described in the original article mentioned in this post, however it appears that the sort of XML file I wish to edit does not lend itself to this way of doing things

Screen shot 2012-01-23 at 11.40.11 AM.png
When values are changed in the Text Editor, they are reflected in the Form Editor and vice versa. Undo actions work on the Text Editor and Text entry fields in the Form Editor (but not on the combo box alas)

I hope this example helps others. Thanks to Peter from JetBrains for his help.

C



Attachment(s):
XMLDomExample.zip
0

Please sign in to leave a comment.