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