Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » JFace » [DATABINDING] How to create bindings for TreeViewer with Model containing Maps and nested list (Example of how to use the databinding API to observe maps and elements inside the map. )
[DATABINDING] How to create bindings for TreeViewer with Model containing Maps and nested list [message #1061248] Thu, 30 May 2013 18:43 Go to next message
Marcel Becker is currently offline Marcel Becker
Messages: 2
Registered: May 2013
Junior Member
Hi,

After going through all the databinding tutorials I could find, going through the snippets code, and reading the javadoc for the databinding APIs, I finally think I know enough to post my question.

My main question is how to use the databinding API to get a tree viewer to update the label of a tree node once a field in a deeply nested model object changes. The model uses a mixture of set and maps. The elements that change may be inside a list or a map and deep down the model hierarchy.

Assume my model has the following structure:
public class ModelChangeSupport {
 // Standard implementation for property change support. 
...
}

// Auxiliary enum types to define valid status values. 
public enum IssueStatus { SAFE, UNSAFE, UNRESOLVED }
public enum LocationIssueStatus { APPROVED, UNRESOLVED }

/** Top level model class. 
 *  Has a Set and a Map we want to observe. 
 */
public class SecuritySpecModel extends ModelChangeSupport {
  /** Set of methods that are considered safe. */
  private Set<MethodModel> whitelist = new LinkedHashSet<MethodModel>();

  /** Default security spec hierarchy. 
   *  A top level method, the key of the map, maps to a list of child methods. 
   *  Each child method has a list of code locations. 
   */
  private Map<MethodModel, List<MethodModel>> inputEvents =
      new LinkedHashMap<MethodModel, List<MethodModel>>(); 

// Getter and setters for whitelist and inputEvents
}

/** Model object with a status field and a list of code locations we want to 
 *  observe. 
*/
public class MethodModel extends ModelChangeSupport { 
private String methodName;
  private String className;
  private String returnType;
  private String methodSignature;
  private CodeLocationModel declarationLocation;
  private List<CodeLocationModel> lines = new ArrayList<CodeLocationModel>();
  /**
   * Current status of the code location.
   */
  private IssueStatus status = IssueStatus.UNRESOLVED;

// Getter and setter for all fields. 

// Setter for status. 
public void setStatus(IssueStatus newStatus) {
    firePropertyChange("status", this.status, this.status = newStatus);
  }
}

/** The information about the location of a method in a file. 
 *  Each method has a list of CodeLocations. 
 *  We want to observe the status field of these objects. 
*/
public class CodeLocationModel extends ModelChangeSupport {
  /**
   * Current status of the code location.
   */
  private LocationIssueStatus status = LocationIssueStatus.UNRESOLVED;

  // Setter for status. 
  public void setStatus(LocationIssueStatus newStatus) {
    firePropertyChange("status", this.status, this.status = newStatus);
  }
}


In summary I have a top level spec model (think of a java compilation unit) that has a set of methods, and a map from methods to a list of methods. Each method has a status, and a list of code locations where the method is called. Each method and each code location has some strings and a status field.

My outline or tree viewer show a structure like this:
- Whitelist
-- Method_1
-- Method_2
...
- Input Event: Method_A
-- Output Event: Method_A1
*** Code Location: Location_1
*** Code Location: Location_2
- Input Event: Method_B
-- Output Event : Method_B1
*** Location_3
*** Location_4

For my tree viewer label provider I am using a StyledCellLabelProvider.

What I want:
1. The status of methods and code location will be changed either by a UI command or programmatically.
2. As the status changes, I want the style of the label for the corresponding method or code location to change -- strikeout the label and change icons.
3. The inputEvents map and the whitelist set of the top level model object can also change programmatically.
4. As items are added or removed from the different sets and lists in the model objects, I want the viewer to update/refresh.

My Questions:
1. How to define the observables and bindings so I can track additions and removals to both the whitelist set and the inputEvents map?

2. How to define observables and bindings so I can respond to changes in status values for any methods or code locations.


I understand I have to define my ContentProvider as:

private void initializeTreeViewer() {
      this.viewer = new TreeViewer(parentComposite, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
      SecuritySpecModel securitySpecModel = new SecuritySpecModel(..);
      ObservableListTreeContentProvider cp =
          new ObservableListTreeContentProvider(
                  new TreeIObservableFactoryImpl(securitySpecModel),
                  new TreeStructureAdvisorImpl());
      viewer.setContentProvider(cp);
      
      // Code to set the label provider attributes 
      // Code to create observables and bindings. 

      viewer.setLabelProvider( /* some label provider object */)
      viewer.setInput(securitySpecModel); 

}


3. I do not know how to write the IObservableFactory.createObservable() method.
I do not understand how or when the create observable method is called, and what should be returned by it.

Next there is the definition of the Observable objects ( and bindings?) to be used by the label provider.

 viewer.setLabelProvider(
  new ObservableTreeElementLabelProvider(allLabelProviderAttributeMaps??));



4. What are these observable attribute maps that I need to pass to my label provider to get the label provider to work correctly?

5. Finally, how do I get the view to update/refresh once the status of one of the objects change? How do I define the bindings between my viewer and all the observables I want: whitelist set, input event map, method code location list, method status, and code location status.


I see examples that use DataBindingContext.bindList(), others use ViewerSupport.bind(), but I could not figure out which one to use and how to define the correct arguments to make the bind work correctly.

Here are some examples of things that I tried but did not work:

  IObservableMap[] specLabelProviderMaps =
          BeansObservables.observeMaps(cp.getKnownElements(), 
              SecuritySpecModel.class,
              new String[] {"inputEventBlocks", "whitelist"});

      IObservableMap[] methodLabelProviderMaps =
          BeansObservables.observeMaps(cp.getKnownElements(), 
                                       MethodModel.class,
                                       new String[] {"status"});

      IObservableMap[] codeLocationLabelProviderMaps =
          BeansObservables.observeMaps(cp.getKnownElements(), 
                                       CodeLocationModel.class,
                                       new String[] {"status"});


      IObservableMap[] allLabelProviderMaps =
          new IObservableMap[specLabelProviderMaps.length + 
                             methodLabelProviderMaps.length +
                             codeLocationLabelProviderMaps.length];

      int count = 0;
      for (int i = 0; i < specLabelProviderMaps.length; i++) {
        allLabelProviderMaps[count++] = specLabelProviderMaps[i];
      }
      for (int i = 0; i < methodLabelProviderMaps.length; i++) {
        allLabelProviderMaps[count++] = methodLabelProviderMaps[i];
      }
      for (int i = 0; i < codeLocationLabelProviderMaps.length; i++) {
        allLabelProviderMaps[count++] = codeLocationLabelProviderMaps[i];
      }
      viewer.setLabelProvider(
            new ObservableTreeElementLabelProvider(allLabelProviderMaps));





Thank you,
Marcel





Re: [DATABINDING] How to create bindings for TreeViewer with Model containing Maps and nested list [message #1061504 is a reply to message #1061248] Sat, 01 June 2013 09:51 Go to previous message
Nigel Westbury is currently offline Nigel Westbury
Messages: 16
Registered: July 2009
Junior Member
Hi Marcel,

I'll answer question 3 first. The IObservableFactory.createObservable()
method takes as a parameter a target object and returns an observable on
a property of that target. In this case the target is a node in the
tree and the property is the list of child nodes. So the returned
value, being an observable on the list of child nodes, would be of type
IObservableList.

Because the observable returned by your factory is an IObservableList,
one can listen to it for additions and removals. This is exactly what
the ObservableListTreeContentProvider content provider does. So to
answer question 1, you make sure that the IObservableList objects
returned by your factory do indeed correctly process additions and
removals and the content provider will pass on these changes.

Moving on to the labels. While you can set a single label provider on
the viewer, that is legacy code. It is better to set a separate label
provider on each column. That way you don't need switch statements that
switch of a column index passed in by JFace. So don't call
Viewer.setLabelProvider. Instead use TableViewerColumn to create each
column and call the setLabelProvider method and pass a
ObservableMapCellLabelProvider object.

In your case you need more than just the text when setting the
properties of a cell. You also need a boolean to indicate if the text
is striked out, and you need an icon. If you look at
ObservableMapCellLabelProvider you will see that it can actually take an
array of maps. So you would need an array of three maps. You will have
to derive a class from ObservableMapCellLabelProvider (the constructor
that takes an array of maps is protected) and implement the update
method to do the strike out and whatever else you need.

I'm not sure what your intention was when concatenating those arrays of
maps but it is probably not necessary.

Nigel Westbury


On 30/05/2013 21:42, Marcel Becker wrote:
> Hi,
> After going through all the databinding tutorials I could find, going
> through the snippets code, and reading the javadoc for the databinding
> APIs, I finally think I know enough to post my question.
> My main question is how to use the databinding API to get a tree viewer
> to update the label of a tree node once a field in a deeply nested model
> object changes. The model uses a mixture of set and maps. The elements
> that change may be inside a list or a map and deep down the model
> hierarchy.
> Assume my model has the following structure:
> public class ModelChangeSupport {
> // Standard implementation for property change support. ..
> }
>
> // Auxiliary enum types to define valid status values. public enum
> IssueStatus { SAFE, UNSAFE, UNRESOLVED }
> public enum LocationIssueStatus { APPROVED, UNRESOLVED }
>
> /** Top level model class. * Has a Set and a Map we want to observe. */
> public class SecuritySpecModel extends ModelChangeSupport {
> /** Set of methods that are considered safe. */
> private Set<MethodModel> whitelist = new LinkedHashSet<MethodModel>();
>
> /** Default security spec hierarchy. * A top level method, the key
> of the map, maps to a list of child methods. * Each child method has
> a list of code locations. */
> private Map<MethodModel, List<MethodModel>> inputEvents =
> new LinkedHashMap<MethodModel, List<MethodModel>>();
> // Getter and setters for whitelist and inputEvents
> }
>
> /** Model object with a status field and a list of code locations we
> want to * observe. */
> public class MethodModel extends ModelChangeSupport { private String
> methodName;
> private String className;
> private String returnType;
> private String methodSignature;
> private CodeLocationModel declarationLocation;
> private List<CodeLocationModel> lines = new
> ArrayList<CodeLocationModel>();
> /**
> * Current status of the code location.
> */
> private IssueStatus status = IssueStatus.UNRESOLVED;
>
> // Getter and setter for all fields.
> // Setter for status. public void setStatus(IssueStatus newStatus) {
> firePropertyChange("status", this.status, this.status = newStatus);
> }
> }
>
> /** The information about the location of a method in a file. * Each
> method has a list of CodeLocations. * We want to observe the status
> field of these objects. */
> public class CodeLocationModel extends ModelChangeSupport {
> /**
> * Current status of the code location.
> */
> private LocationIssueStatus status = LocationIssueStatus.UNRESOLVED;
>
> // Setter for status. public void setStatus(LocationIssueStatus
> newStatus) {
> firePropertyChange("status", this.status, this.status = newStatus);
> }
> }
>
>
> In summary I have a top level spec model (think of a java compilation
> unit) that has a set of methods, and a map from methods to a list of
> methods. Each method has a status, and a list of code locations where
> the method is called. Each method and each code location has some
> strings and a status field.
> My outline or tree viewer show a structure like this: - Whitelist --
> Method_1 -- Method_2 ...
> - Input Event: Method_A
> -- Output Event: Method_A1 *** Code Location: Location_1 ***
> Code Location: Location_2
> - Input Event: Method_B
> -- Output Event : Method_B1 *** Location_3
> *** Location_4
>
> For my tree viewer label provider I am using a StyledCellLabelProvider.
> What I want: 1. The status of methods and code location will be changed
> either by a UI command or programmatically.
> 2. As the status changes, I want the style of the label for the
> corresponding method or code location to change -- strikeout the label
> and change icons. 3. The inputEvents map and the whitelist set of the
> top level model object can also change programmatically. 4. As items are
> added or removed from the different sets and lists in the model objects,
> I want the viewer to update/refresh.
> My Questions:
> 1. How to define the observables and bindings so I can track additions
> and removals to both the whitelist set and the inputEvents map?
> 2. How to define observables and bindings so I can respond to changes in
> status values for any methods or code locations.
>
>
> I understand I have to define my ContentProvider as:
>
> private void initializeTreeViewer() {
> this.viewer = new TreeViewer(parentComposite, SWT.MULTI |
> SWT.H_SCROLL | SWT.V_SCROLL);
> SecuritySpecModel securitySpecModel = new SecuritySpecModel(..);
> ObservableListTreeContentProvider cp =
> new ObservableListTreeContentProvider(
> new TreeIObservableFactoryImpl(securitySpecModel),
> new TreeStructureAdvisorImpl());
> viewer.setContentProvider(cp);
> // Code to set the label provider attributes // Code to
> create observables and bindings.
> viewer.setLabelProvider( /* some label provider object */)
> viewer.setInput(securitySpecModel);
> }
>
>
> 3. I do not know how to write the IObservableFactory.createObservable()
> method. I do not understand how or when the create observable method is
> called, and what should be returned by it.
> Next there is the definition of the Observable objects ( and bindings?)
> to be used by the label provider.
>
> viewer.setLabelProvider(
> new ObservableTreeElementLabelProvider(allLabelProviderAttributeMaps??));
>
>
>
> 4. What are these observable attribute maps that I need to pass to my
> label provider to get the label provider to work correctly?
>
> 5. Finally, how do I get the view to update/refresh once the status of
> one of the objects change? How do I define the bindings between my
> viewer and all the observables I want: whitelist set, input event map,
> method code location list, method status, and code location status.
>
> I see examples that use DataBindingContext.bindList(), others use
> ViewerSupport.bind(), but I could not figure out which one to use and
> how to define the correct arguments to make the bind work correctly.
> Here are some examples of things that I tried but did not work:
>
> IObservableMap[] specLabelProviderMaps =
> BeansObservables.observeMaps(cp.getKnownElements(),
> SecuritySpecModel.class,
> new String[] {"inputEventBlocks", "whitelist"});
>
> IObservableMap[] methodLabelProviderMaps =
> BeansObservables.observeMaps(cp.getKnownElements(),
> MethodModel.class,
> new String[] {"status"});
>
> IObservableMap[] codeLocationLabelProviderMaps =
> BeansObservables.observeMaps(cp.getKnownElements(),
> CodeLocationModel.class,
> new String[] {"status"});
>
>
> IObservableMap[] allLabelProviderMaps =
> new IObservableMap[specLabelProviderMaps.length +
> methodLabelProviderMaps.length +
> codeLocationLabelProviderMaps.length];
>
> int count = 0;
> for (int i = 0; i < specLabelProviderMaps.length; i++) {
> allLabelProviderMaps[count++] = specLabelProviderMaps[i];
> }
> for (int i = 0; i < methodLabelProviderMaps.length; i++) {
> allLabelProviderMaps[count++] = methodLabelProviderMaps[i];
> }
> for (int i = 0; i < codeLocationLabelProviderMaps.length; i++) {
> allLabelProviderMaps[count++] = codeLocationLabelProviderMaps[i];
> }
> viewer.setLabelProvider(
> new ObservableTreeElementLabelProvider(allLabelProviderMaps));
>
>
>
>
>
> Thank you, Marcel
>
>
>
>
>
>
Previous Topic:[CheckboxTreeViewer]how to fire events to check state listeners
Next Topic:TreeViewer doesn't work well on Ubuntu
Goto Forum:
  


Current Time: Sat Oct 25 01:33:07 GMT 2014

Powered by FUDForum. Page generated in 0.16042 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software