001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2016 ForgeRock AS.
016 */
017package org.opends.guitools.controlpanel.ui;
018
019import static org.opends.messages.AdminToolMessages.*;
020
021import java.awt.Component;
022import java.awt.GridBagConstraints;
023import java.awt.GridBagLayout;
024import java.awt.datatransfer.Transferable;
025import java.awt.datatransfer.UnsupportedFlavorException;
026import java.awt.dnd.DnDConstants;
027import java.awt.dnd.DropTarget;
028import java.awt.dnd.DropTargetDragEvent;
029import java.awt.dnd.DropTargetDropEvent;
030import java.awt.dnd.DropTargetEvent;
031import java.awt.dnd.DropTargetListener;
032import java.awt.event.ActionEvent;
033import java.awt.event.ActionListener;
034import java.io.IOException;
035import java.util.ArrayList;
036import java.util.LinkedHashSet;
037
038import javax.swing.ButtonGroup;
039import javax.swing.JButton;
040import javax.swing.JLabel;
041import javax.swing.JPanel;
042import javax.swing.JRadioButton;
043import javax.swing.JScrollPane;
044import javax.swing.JTextArea;
045import javax.swing.JTextField;
046import javax.swing.SwingUtilities;
047import javax.swing.event.DocumentEvent;
048import javax.swing.event.DocumentListener;
049
050import org.forgerock.i18n.LocalizableMessage;
051import org.forgerock.i18n.LocalizedIllegalArgumentException;
052import org.forgerock.opendj.ldap.DN;
053import org.opends.guitools.controlpanel.browser.BrowserController;
054import org.opends.guitools.controlpanel.ui.nodes.BasicNode;
055import org.opends.guitools.controlpanel.ui.nodes.BrowserNodeInfo;
056import org.opends.guitools.controlpanel.ui.nodes.DndBrowserNodes;
057import org.opends.guitools.controlpanel.util.Utilities;
058import org.opends.server.types.LDAPURL;
059import org.opends.server.types.OpenDsException;
060import org.opends.server.util.ServerConstants;
061
062/** The panel to create a group. */
063public class NewGroupPanel extends AbstractNewEntryPanel
064{
065  private static final long serialVersionUID = -8173120152617813282L;
066  private JLabel lName = Utilities.createPrimaryLabel();
067  private JLabel lDescription = Utilities.createPrimaryLabel();
068  private JLabel lMembers = Utilities.createPrimaryLabel();
069  private JLabel lDn = Utilities.createPrimaryLabel();
070
071  private JLabel lMemberDNs;
072  private JLabel lLDAPURL;
073  private JLabel lReferenceGroup;
074
075  private JLabel[] labels = {lName, lDescription, lMembers, lDn};
076
077  private JTextField name = Utilities.createLongTextField();
078  private JTextField description = Utilities.createLongTextField();
079  private JRadioButton dynamicGroup;
080  private JRadioButton staticGroup;
081  private JRadioButton virtualGroup;
082
083  private JTextArea staticMembers;
084  private JButton addMembers;
085  private JTextField filter  = Utilities.createLongTextField();
086  private JTextField referenceGroup  = Utilities.createLongTextField();
087  private JButton browseReferenceGroup;
088
089  private GenericDialog membersDlg;
090  private LDAPEntrySelectionPanel membersPanel;
091
092  private GenericDialog referenceGroupDlg;
093  private LDAPEntrySelectionPanel referenceGroupPanel;
094
095  private JLabel dn = Utilities.createDefaultLabel();
096
097  /** An array containing the fields of this panel. */
098  private final JTextField[] fields = { name, description, filter, referenceGroup };
099
100  /** Default constructor. */
101  public NewGroupPanel()
102  {
103    super();
104    createLayout();
105  }
106
107  @Override
108  public void setParent(BasicNode parentNode, BrowserController controller)
109  {
110    super.setParent(parentNode, controller);
111    dn.setText(","+parentNode.getDN());
112    for (JTextField tf : fields)
113    {
114      tf.setText("");
115    }
116    staticMembers.setText("");
117    filter.setText("ldap:///"+parentNode.getDN()+"??sub?(<your filter>)");
118  }
119
120  @Override
121  public LocalizableMessage getTitle()
122  {
123    return INFO_CTRL_PANEL_NEW_GROUP_PANEL_TITLE.get();
124  }
125
126  @Override
127  public Component getPreferredFocusComponent()
128  {
129    return name;
130  }
131
132  @Override
133  protected LocalizableMessage getProgressDialogTitle()
134  {
135    return INFO_CTRL_PANEL_NEW_GROUP_PANEL_TITLE.get();
136  }
137
138  @Override
139  protected boolean checkSyntaxBackground()
140  {
141    return staticGroup.isSelected();
142  }
143
144  @Override
145  protected void checkSyntax(ArrayList<LocalizableMessage> errors)
146  {
147    Runnable runnable = new Runnable()
148    {
149      @Override
150      public void run()
151      {
152        for (JLabel label : labels)
153        {
154          setPrimaryValid(label);
155        }
156        setSecondaryValid(lMemberDNs);
157        setSecondaryValid(lLDAPURL);
158        setSecondaryValid(lReferenceGroup);
159      }
160    };
161    if (checkSyntaxBackground())
162    {
163      SwingUtilities.invokeLater(runnable);
164    }
165    else
166    {
167      runnable.run();
168    }
169
170    JTextField[] requiredFields = {name};
171    LocalizableMessage[] msgs = {ERR_CTRL_PANEL_NAME_OF_GROUP_REQUIRED.get()};
172    for (int i=0; i<requiredFields.length; i++)
173    {
174      String v = requiredFields[i].getText().trim();
175      if (v.length() == 0)
176      {
177        errors.add(msgs[i]);
178      }
179    }
180
181    if (staticGroup.isSelected())
182    {
183      String[] members = staticMembers.getText().split("\n");
184      boolean oneMemberDefined = false;
185      boolean errorFound = false;
186      for (String member : members)
187      {
188        member = member.trim();
189        if (member.length() > 0)
190        {
191          try
192          {
193            DN.valueOf(member);
194            if (!entryExists(member))
195            {
196              errorFound = true;
197              errors.add(ERR_CTRL_PANEL_MEMBER_NOT_FOUND.get(member));
198            }
199            else
200            {
201              oneMemberDefined = true;
202            }
203          }
204          catch (LocalizedIllegalArgumentException e)
205          {
206            errorFound = true;
207            errors.add(ERR_CTRL_PANEL_MEMBER_VALUE_NOT_VALID.get(member, e.getMessageObject()));
208          }
209        }
210      }
211      if (!oneMemberDefined && !errorFound)
212      {
213        errorFound = true;
214        errors.add(ERR_CTRL_PANEL_MEMBER_REQUIRED.get());
215      }
216      if (errorFound)
217      {
218        SwingUtilities.invokeLater(new Runnable()
219        {
220          @Override
221          public void run()
222          {
223            setSecondaryInvalid(lMemberDNs);
224            setPrimaryInvalid(lMembers);
225          }
226        });
227      }
228    }
229    else if (dynamicGroup.isSelected())
230    {
231      boolean errorFound = false;
232      String f = filter.getText().trim();
233      if (f.length() == 0)
234      {
235        errors.add(ERR_CTRL_PANEL_GROUP_FILTER_REQUIRED.get());
236        errorFound = true;
237      }
238      else
239      {
240        try
241        {
242          LDAPURL.decode(f, true);
243        }
244        catch (LocalizedIllegalArgumentException | OpenDsException e)
245        {
246          errors.add(ERR_CTRL_PANEL_GROUP_FILTER_NOT_VALID.get(e.getMessageObject()));
247        }
248      }
249      if (errorFound)
250      {
251        setSecondaryInvalid(lLDAPURL);
252        setPrimaryInvalid(lMembers);
253      }
254    }
255    else
256    {
257      boolean errorFound = false;
258      String ref = referenceGroup.getText().trim();
259      try
260      {
261        DN.valueOf(ref);
262        if (!entryExists(ref))
263        {
264          errorFound = true;
265          errors.add(ERR_CTRL_PANEL_REFERENCE_GROUP_NOT_FOUND.get());
266        }
267        else if (!hasObjectClass(ref, ServerConstants.OC_GROUP_OF_URLS))
268        {
269          errorFound = true;
270          errors.add(ERR_CTRL_PANEL_REFERENCE_GROUP_NOT_DYNAMIC.get());
271        }
272      }
273      catch (LocalizedIllegalArgumentException e)
274      {
275        errorFound = true;
276        errors.add(ERR_CTRL_PANEL_REFERENCE_GROUP_NOT_VALID.get(e.getMessageObject()));
277      }
278      if (errorFound)
279      {
280        setSecondaryInvalid(lReferenceGroup);
281        setPrimaryInvalid(lMembers);
282      }
283    }
284  }
285
286  /** Creates the layout of the panel (but the contents are not populated here). */
287  private void createLayout()
288  {
289    GridBagConstraints gbc = new GridBagConstraints();
290    LocalizableMessage[] ls = {
291        INFO_CTRL_PANEL_NEW_GROUP_NAME_LABEL.get(),
292        INFO_CTRL_PANEL_NEW_GROUP_DESCRIPTION_LABEL.get(),
293        INFO_CTRL_PANEL_NEW_GROUP_MEMBERS_LABEL.get(),
294        INFO_CTRL_PANEL_NEW_GROUP_ENTRY_DN_LABEL.get()
295        };
296    int i = 0;
297    for (LocalizableMessage l : ls)
298    {
299      labels[i].setText(l.toString());
300      i++;
301    }
302    Utilities.setRequiredIcon(lName);
303    Utilities.setRequiredIcon(lMembers);
304
305    gbc.gridwidth = 2;
306    gbc.gridy = 0;
307    addErrorPane(gbc);
308
309    gbc.gridy ++;
310    gbc.gridwidth = 1;
311    gbc.weighty = 0.0;
312    gbc.gridx = 1;
313    gbc.anchor = GridBagConstraints.EAST;
314    gbc.fill = GridBagConstraints.NONE;
315    JLabel requiredLabel = createRequiredLabel();
316    gbc.insets.bottom = 10;
317    add(requiredLabel, gbc);
318
319    gbc.gridy ++;
320    gbc.fill = GridBagConstraints.HORIZONTAL;
321    gbc.anchor = GridBagConstraints.WEST;
322    gbc.insets.bottom = 0;
323
324    staticGroup = Utilities.createRadioButton(
325        INFO_CTRL_PANEL_STATIC_GROUP_LABEL.get());
326    dynamicGroup = Utilities.createRadioButton(
327        INFO_CTRL_PANEL_DYNAMIC_GROUP_LABEL.get());
328    virtualGroup = Utilities.createRadioButton(
329        INFO_CTRL_PANEL_VIRTUAL_STATIC_GROUP_LABEL.get());
330    ButtonGroup group = new ButtonGroup();
331    group.add(staticGroup);
332    group.add(dynamicGroup);
333    group.add(virtualGroup);
334    staticGroup.setSelected(true);
335    ActionListener actionListener = new ActionListener()
336    {
337      @Override
338      public void actionPerformed(ActionEvent ev)
339      {
340        checkEnabling();
341      }
342    };
343    staticGroup.addActionListener(actionListener);
344    dynamicGroup.addActionListener(actionListener);
345    virtualGroup.addActionListener(actionListener);
346
347    JLabel[] labels = {lName, lDescription, lMembers};
348    Component[] comps = {name, description, staticGroup};
349    Component[] inlineHelp = {null, null, null};
350
351    for (i=0 ; i< labels.length; i++)
352    {
353      gbc.insets.left = 0;
354      gbc.weightx = 0.0;
355      gbc.gridx = 0;
356      gbc.gridwidth = 1;
357      add(labels[i], gbc);
358      gbc.insets.left = 10;
359      gbc.weightx = 1.0;
360      gbc.gridx = 1;
361      add(comps[i], gbc);
362      if (inlineHelp[i] != null)
363      {
364        gbc.insets.top = 3;
365        gbc.gridy ++;
366        add(inlineHelp[i], gbc);
367      }
368      gbc.insets.top = 10;
369      gbc.gridy ++;
370    }
371    gbc.insets.top = 5;
372    lMemberDNs = Utilities.createDefaultLabel(
373        INFO_CTRL_PANEL_GROUP_MEMBER_DNS_LABEL.get());
374    gbc.insets.left = 30;
375    add(lMemberDNs, gbc);
376    staticMembers = Utilities.createTextArea(LocalizableMessage.EMPTY, 8, 40);
377    JScrollPane scroll = Utilities.createScrollPane(staticMembers);
378    gbc.weighty = 1.0;
379    gbc.fill = GridBagConstraints.BOTH;
380    gbc.gridy ++;
381    JPanel p = new JPanel(new GridBagLayout());
382    p.setOpaque(false);
383    lLDAPURL = Utilities.createDefaultLabel(
384        INFO_CTRL_PANEL_GROUP_FILTER_LABEL.get());
385    add(p, gbc);
386    GridBagConstraints gbc2 = new GridBagConstraints();
387    gbc2.gridx = 0;
388    gbc2.weightx = 1.0;
389    gbc2.weighty = 1.0;
390    gbc2.fill = GridBagConstraints.BOTH;
391    p.add(scroll, gbc2);
392    gbc2.insets.left = 5;
393    gbc2.weightx = 0.0;
394    gbc2.fill = GridBagConstraints.HORIZONTAL;
395    gbc2.gridx ++;
396    addMembers = Utilities.createButton(
397        INFO_CTRL_PANEL_ADD_MEMBERS_BUTTON.get());
398    gbc2.anchor = GridBagConstraints.NORTH;
399    p.add(addMembers, gbc2);
400
401    addMembers.addActionListener(new ActionListener()
402    {
403      @Override
404      public void actionPerformed(ActionEvent ev)
405      {
406        addMembersClicked();
407      }
408    });
409
410    gbc.insets.left = 10;
411    gbc.insets.top = 10;
412    gbc.fill = GridBagConstraints.HORIZONTAL;
413    gbc.weighty = 0.0;
414    gbc.gridy ++;
415    add(dynamicGroup, gbc);
416    gbc.insets.top = 5;
417    gbc.weightx = 0.0;
418    gbc.gridy ++;
419    p = new JPanel(new GridBagLayout());
420    p.setOpaque(false);
421    gbc.insets.left = 30;
422    add(p, gbc);
423
424    gbc2 = new GridBagConstraints();
425    gbc2.gridx = 0;
426    gbc2.fill = GridBagConstraints.HORIZONTAL;
427    p.add(lLDAPURL, gbc2);
428    gbc2.insets.left = 5;
429    gbc2.weightx = 1.0;
430    gbc2.gridx ++;
431    p.add(filter, gbc2);
432
433    lReferenceGroup = Utilities.createDefaultLabel(
434        INFO_CTRL_PANEL_DYNAMIC_GROUP_REFERENCE_LABEL.get());
435    gbc.insets.left = 30;
436    p = new JPanel(new GridBagLayout());
437    p.setOpaque(false);
438    gbc.gridy ++;
439    add(p, gbc);
440
441    gbc.gridy ++;
442    gbc.gridx = 1;
443    gbc.insets.left = 10;
444    gbc.insets.top = 10;
445    gbc.fill = GridBagConstraints.HORIZONTAL;
446    gbc.weighty = 0.0;
447    add(virtualGroup, gbc);
448    gbc.insets.top = 5;
449    gbc.weightx = 0.0;
450
451    gbc.insets.top = 10;
452    gbc.gridy ++;
453    gbc.insets.left = 0;
454    gbc.weightx = 0.0;
455    gbc.gridx = 0;
456    gbc.gridwidth = 1;
457    add(lDn, gbc);
458    gbc.insets.left = 10;
459    gbc.weightx = 1.0;
460    gbc.gridx = 1;
461    add(dn, gbc);
462
463    gbc2 = new GridBagConstraints();
464    gbc2.gridx = 0;
465    gbc2.fill = GridBagConstraints.HORIZONTAL;
466    p.add(lReferenceGroup, gbc2);
467    gbc2.insets.left = 5;
468    gbc2.weightx = 1.0;
469    gbc2.gridx ++;
470    p.add(referenceGroup, gbc2);
471    gbc2.weightx = 0.0;
472    gbc2.gridx ++;
473    browseReferenceGroup =
474      Utilities.createButton(INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get());
475    browseReferenceGroup.addActionListener(new ActionListener()
476    {
477      @Override
478      public void actionPerformed(ActionEvent ev)
479      {
480        browseReferenceGroupClicked();
481      }
482    });
483    p.add(browseReferenceGroup, gbc2);
484
485    DocumentListener listener = new DocumentListener()
486    {
487      @Override
488      public void insertUpdate(DocumentEvent ev)
489      {
490        updateDNValue();
491      }
492
493      @Override
494      public void changedUpdate(DocumentEvent ev)
495      {
496        insertUpdate(ev);
497      }
498
499      @Override
500      public void removeUpdate(DocumentEvent ev)
501      {
502        insertUpdate(ev);
503      }
504    };
505    JTextField[] toAddListener = {name};
506    for (JTextField tf : toAddListener)
507    {
508      tf.getDocument().addDocumentListener(listener);
509    }
510
511    DropTargetListener dropTargetlistener = new DropTargetListener()
512    {
513      @Override
514      public void dragEnter(DropTargetDragEvent e)
515      {
516      }
517
518      @Override
519      public void dragExit(DropTargetEvent e)
520      {
521      }
522
523      @Override
524      public void dragOver(DropTargetDragEvent e)
525      {
526      }
527
528      @Override
529      public void dropActionChanged(DropTargetDragEvent e)
530      {
531      }
532
533      @Override
534      public void drop(DropTargetDropEvent e)
535      {
536        try {
537          Transferable tr = e.getTransferable();
538
539          //flavor not supported, reject drop
540          if (!tr.isDataFlavorSupported(DndBrowserNodes.INFO_FLAVOR))
541          {
542            e.rejectDrop();
543          }
544
545          //cast into appropriate data type
546          DndBrowserNodes nodes =
547            (DndBrowserNodes) tr.getTransferData(DndBrowserNodes.INFO_FLAVOR);
548
549          Component comp = e.getDropTargetContext().getComponent();
550          if (comp == staticMembers)
551          {
552            StringBuilder sb = new StringBuilder();
553            sb.append(staticMembers.getText());
554            for (BrowserNodeInfo node : nodes.getNodes())
555            {
556              if (sb.length() > 0)
557              {
558                sb.append("\n");
559              }
560              sb.append(node.getNode().getDN());
561            }
562            staticMembers.setText(sb.toString());
563            staticMembers.setCaretPosition(sb.length());
564          }
565          else if (comp == referenceGroup
566              && nodes.getNodes().length > 0)
567          {
568            String dn = nodes.getNodes()[0].getNode().getDN();
569            referenceGroup.setText(dn);
570            referenceGroup.setCaretPosition(dn.length());
571          }
572          e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
573          e.getDropTargetContext().dropComplete(true);
574        }
575        catch (IOException | UnsupportedFlavorException io)
576        {
577          e.rejectDrop();
578        }
579      }
580    };
581    new DropTarget(staticMembers, dropTargetlistener);
582    new DropTarget(referenceGroup, dropTargetlistener);
583
584    checkEnabling();
585  }
586
587  private void updateDNValue()
588  {
589    String value = name.getText().trim();
590    if (value.length() > 0)
591    {
592      dn.setText("cn" + "=" + value + "," + parentNode.getDN());
593    }
594    else
595    {
596      dn.setText(","+parentNode.getDN());
597    }
598  }
599
600  private void addMembersClicked()
601  {
602    if (membersDlg == null)
603    {
604      membersPanel = new LDAPEntrySelectionPanel();
605      membersPanel.setTitle(INFO_CTRL_PANEL_ADD_MEMBERS_LABEL.get());
606      membersPanel.setFilter(LDAPEntrySelectionPanel.Filter.USERS);
607      membersPanel.setMultipleSelection(true);
608      membersPanel.setInfo(getInfo());
609      membersDlg = new GenericDialog(Utilities.getFrame(this), membersPanel);
610      Utilities.centerGoldenMean(membersDlg,
611          Utilities.getParentDialog(this));
612      membersDlg.setModal(true);
613    }
614    membersDlg.setVisible(true);
615    String[] dns = membersPanel.getDNs();
616    if (dns.length > 0)
617    {
618      StringBuilder sb = new StringBuilder();
619      sb.append(staticMembers.getText());
620      for (String dn : dns)
621      {
622        if (sb.length() > 0)
623        {
624          sb.append("\n");
625        }
626        sb.append(dn);
627      }
628      staticMembers.setText(sb.toString());
629      staticMembers.setCaretPosition(sb.length());
630    }
631  }
632
633  private void browseReferenceGroupClicked()
634  {
635    if (referenceGroupDlg == null)
636    {
637      referenceGroupPanel = new LDAPEntrySelectionPanel();
638      referenceGroupPanel.setTitle(
639          INFO_CTRL_PANEL_CHOOSE_REFERENCE_GROUP.get());
640      referenceGroupPanel.setFilter(
641          LDAPEntrySelectionPanel.Filter.DYNAMIC_GROUPS);
642      referenceGroupPanel.setMultipleSelection(false);
643      referenceGroupPanel.setInfo(getInfo());
644      referenceGroupDlg = new GenericDialog(Utilities.getFrame(this),
645          referenceGroupPanel);
646      Utilities.centerGoldenMean(referenceGroupDlg,
647          Utilities.getParentDialog(this));
648      referenceGroupDlg.setModal(true);
649    }
650    referenceGroupDlg.setVisible(true);
651    String[] dns = referenceGroupPanel.getDNs();
652    if (dns.length > 0)
653    {
654      referenceGroup.setText(dns[0]);
655    }
656  }
657
658  @Override
659  protected String getLDIF()
660  {
661    StringBuilder sb = new StringBuilder();
662    sb.append("dn: ").append(dn.getText()).append("\n");
663
664    String[] attrNames = {"cn", "description"};
665    JTextField[] textFields = {name, description};
666    for (int i=0; i<attrNames.length; i++)
667    {
668      String value = textFields[i].getText().trim();
669      if (value.length() > 0)
670      {
671        sb.append(attrNames[i]).append(": ").append(value).append("\n");
672      }
673    }
674
675    sb.append("objectclass: top\n");
676    if (staticGroup.isSelected())
677    {
678      sb.append("objectClass: ").append(ServerConstants.OC_GROUP_OF_NAMES);
679      String[] members = staticMembers.getText().split("\n");
680      LinkedHashSet<DN> dns = new LinkedHashSet<>();
681      for (String member : members)
682      {
683        member = member.trim();
684        if (member.length() > 0)
685        {
686          dns.add(DN.valueOf(member));
687        }
688      }
689
690      for (DN dn : dns)
691      {
692        sb.append("\n").append(ServerConstants.ATTR_MEMBER)
693          .append(": ").append(dn);
694      }
695    }
696    else if (dynamicGroup.isSelected())
697    {
698      sb.append("objectClass: ").append(ServerConstants.OC_GROUP_OF_URLS)
699        .append("\n");
700      sb.append(ServerConstants.ATTR_MEMBER_URL).append(": ")
701        .append(filter.getText().trim());
702    }
703    else
704    {
705      sb.append("objectClass: ds-virtual-static-group\n");
706      sb.append("objectClass: ").append(ServerConstants.OC_GROUP_OF_NAMES)
707        .append("\n");
708      sb.append("ds-target-group-dn: ").append(referenceGroup.getText().trim());
709    }
710
711    return sb.toString();
712  }
713
714  private void checkEnabling()
715  {
716    staticMembers.setEnabled(staticGroup.isSelected());
717    addMembers.setEnabled(staticGroup.isSelected());
718    filter.setEnabled(dynamicGroup.isSelected());
719    referenceGroup.setEnabled(virtualGroup.isSelected());
720    browseReferenceGroup.setEnabled(virtualGroup.isSelected());
721
722    lMemberDNs.setEnabled(staticGroup.isSelected());
723    lLDAPURL.setEnabled(dynamicGroup.isSelected());
724    lReferenceGroup.setEnabled(virtualGroup.isSelected());
725  }
726}