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 2014-2016 ForgeRock AS.
016 */
017package org.opends.guitools.controlpanel.ui;
018
019import static org.opends.messages.AdminToolMessages.*;
020import static org.opends.server.types.CommonSchemaElements.*;
021import static org.opends.server.util.CollectionUtils.*;
022
023import java.awt.Component;
024import java.awt.Container;
025import java.awt.GridBagConstraints;
026import java.awt.GridBagLayout;
027import java.awt.Insets;
028import java.awt.event.ActionEvent;
029import java.awt.event.ActionListener;
030import java.io.File;
031import java.util.ArrayList;
032import java.util.Collection;
033import java.util.Collections;
034import java.util.Comparator;
035import java.util.HashMap;
036import java.util.HashSet;
037import java.util.LinkedHashSet;
038import java.util.List;
039import java.util.Map;
040import java.util.Set;
041
042import javax.swing.DefaultComboBoxModel;
043import javax.swing.JButton;
044import javax.swing.JCheckBox;
045import javax.swing.JComboBox;
046import javax.swing.JLabel;
047import javax.swing.JList;
048import javax.swing.JPanel;
049import javax.swing.JScrollPane;
050import javax.swing.JTextField;
051import javax.swing.ListCellRenderer;
052import javax.swing.SwingUtilities;
053import javax.swing.event.ChangeEvent;
054import javax.swing.event.ChangeListener;
055import javax.swing.event.DocumentEvent;
056import javax.swing.event.DocumentListener;
057import javax.swing.event.ListDataEvent;
058import javax.swing.event.ListDataListener;
059
060import org.forgerock.i18n.LocalizableMessage;
061import org.forgerock.i18n.LocalizableMessageBuilder;
062import org.forgerock.opendj.ldap.schema.AttributeType;
063import org.forgerock.opendj.ldap.schema.ObjectClassType;
064import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
065import org.opends.guitools.controlpanel.datamodel.SortableListModel;
066import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
067import org.opends.guitools.controlpanel.event.ConfigurationElementCreatedListener;
068import org.opends.guitools.controlpanel.event.ScrollPaneBorderListener;
069import org.opends.guitools.controlpanel.event.SuperiorObjectClassesChangedEvent;
070import org.opends.guitools.controlpanel.event.SuperiorObjectClassesChangedListener;
071import org.opends.guitools.controlpanel.task.DeleteSchemaElementsTask;
072import org.opends.guitools.controlpanel.task.ModifyObjectClassTask;
073import org.opends.guitools.controlpanel.task.Task;
074import org.opends.guitools.controlpanel.ui.components.BasicExpander;
075import org.opends.guitools.controlpanel.ui.components.DoubleAddRemovePanel;
076import org.opends.guitools.controlpanel.ui.components.SuperiorObjectClassesEditor;
077import org.opends.guitools.controlpanel.ui.components.TitlePanel;
078import org.opends.guitools.controlpanel.ui.renderer.SchemaElementComboBoxCellRenderer;
079import org.opends.guitools.controlpanel.util.Utilities;
080import org.opends.server.schema.SomeSchemaElement;
081import org.opends.server.types.ObjectClass;
082import org.opends.server.types.Schema;
083import org.opends.server.util.ServerConstants;
084import org.opends.server.util.StaticUtils;
085
086/** The panel that displays a custom object class definition. */
087public class CustomObjectClassPanel extends SchemaElementPanel
088{
089  private static final long serialVersionUID = 2105520588901380L;
090  private JButton delete;
091  private JButton saveChanges;
092  private ObjectClass objectClass;
093  private String ocName;
094  private ScrollPaneBorderListener scrollListener;
095
096  private TitlePanel titlePanel = new TitlePanel(LocalizableMessage.EMPTY, LocalizableMessage.EMPTY);
097  private JLabel lName = Utilities.createPrimaryLabel(
098      INFO_CTRL_PANEL_OBJECTCLASS_NAME_LABEL.get());
099  private JLabel lSuperior = Utilities.createPrimaryLabel(
100      INFO_CTRL_PANEL_OBJECTCLASS_PARENT_LABEL.get());
101  private JLabel lOID = Utilities.createPrimaryLabel(
102      INFO_CTRL_PANEL_OBJECTCLASS_OID_LABEL.get());
103  private JLabel lAliases = Utilities.createPrimaryLabel(
104      INFO_CTRL_PANEL_OBJECTCLASS_ALIASES_LABEL.get());
105  private JLabel lOrigin = Utilities.createPrimaryLabel(
106      INFO_CTRL_PANEL_OBJECTCLASS_ORIGIN_LABEL.get());
107  private JLabel lFile = Utilities.createPrimaryLabel(
108      INFO_CTRL_PANEL_OBJECTCLASS_FILE_LABEL.get());
109  private JTextField aliases = Utilities.createLongTextField();
110  private JLabel lDescription = Utilities.createPrimaryLabel(
111      INFO_CTRL_PANEL_OBJECTCLASS_DESCRIPTION_LABEL.get());
112  private JLabel lType = Utilities.createPrimaryLabel(
113      INFO_CTRL_PANEL_OBJECTCLASS_TYPE_LABEL.get());
114  private JLabel lAttributes = Utilities.createPrimaryLabel(
115      INFO_CTRL_PANEL_OBJECTCLASS_ATTRIBUTES_LABEL.get());
116
117  private Set<AttributeType> inheritedOptionalAttributes = new HashSet<>();
118  private Set<AttributeType> inheritedRequiredAttributes = new HashSet<>();
119
120  private JLabel[] labels = {lName, lSuperior, lOID, lAliases, lOrigin, lFile,
121      lDescription, lType, lAttributes
122  };
123
124  private JTextField name = Utilities.createMediumTextField();
125  private SuperiorObjectClassesEditor superiors =
126    new SuperiorObjectClassesEditor();
127  private JComboBox type = Utilities.createComboBox();
128  private JTextField oid = Utilities.createMediumTextField();
129  private JTextField description = Utilities.createLongTextField();
130  private JTextField origin = Utilities.createLongTextField();
131  private JTextField file = Utilities.createLongTextField();
132  private JCheckBox obsolete = Utilities.createCheckBox(
133      INFO_CTRL_PANEL_OBJECTCLASS_OBSOLETE_LABEL.get());
134  private DoubleAddRemovePanel<AttributeType> attributes;
135
136  private Schema schema;
137  private Set<String> lastAliases = new LinkedHashSet<>();
138
139  private boolean ignoreChangeEvents;
140
141
142  /**
143   * Default constructor of the panel.
144   *
145   */
146  public CustomObjectClassPanel()
147  {
148    super();
149    createLayout();
150  }
151
152  /** {@inheritDoc} */
153  @Override
154  public LocalizableMessage getTitle()
155  {
156    return INFO_CTRL_PANEL_CUSTOM_OBJECTCLASS_TITLE.get();
157  }
158
159  /**
160   * Creates the layout of the panel (but the contents are not populated here).
161   */
162  protected void createLayout()
163  {
164    JPanel p = new JPanel(new GridBagLayout());
165    GridBagConstraints gbc = new GridBagConstraints();
166    p.setOpaque(false);
167    p.setBorder(PANEL_BORDER);
168    createBasicLayout(p, gbc);
169    gbc = new GridBagConstraints();
170    gbc.weightx = 1.0;
171    gbc.weighty = 1.0;
172    gbc.gridwidth = 2;
173    gbc.fill = GridBagConstraints.BOTH;
174    gbc.gridx = 0;
175    gbc.gridy = 0;
176    JScrollPane scroll = Utilities.createBorderLessScrollBar(p);
177    scrollListener =
178      ScrollPaneBorderListener.createBottomBorderListener(scroll);
179    add(scroll, gbc);
180
181    gbc.gridy ++;
182    gbc.weighty = 0.0;
183    gbc.anchor = GridBagConstraints.WEST;
184    gbc.fill = GridBagConstraints.NONE;
185    gbc.insets = new Insets(10, 10, 10, 10);
186    gbc.gridwidth = 1;
187    delete = Utilities.createButton(
188        INFO_CTRL_PANEL_DELETE_OBJECTCLASS_BUTTON.get());
189    delete.setOpaque(false);
190    add(delete, gbc);
191    delete.addActionListener(new ActionListener()
192    {
193      /** {@inheritDoc} */
194      @Override
195      public void actionPerformed(ActionEvent ev)
196      {
197        deleteObjectclass();
198      }
199    });
200
201    gbc.anchor = GridBagConstraints.EAST;
202    gbc.gridx ++;
203    saveChanges =
204      Utilities.createButton(INFO_CTRL_PANEL_SAVE_CHANGES_LABEL.get());
205    saveChanges.setOpaque(false);
206    add(saveChanges, gbc);
207    saveChanges.addActionListener(new ActionListener()
208    {
209      /** {@inheritDoc} */
210      @Override
211      public void actionPerformed(ActionEvent ev)
212      {
213        ArrayList<LocalizableMessage> errors = new ArrayList<>();
214        saveChanges(false, errors);
215      }
216    });
217  }
218
219  /**
220   * Creates the basic layout of the panel.
221   * @param c the container where all the components will be layed out.
222   * @param gbc the grid bag constraints.
223   */
224  protected void createBasicLayout(Container c, GridBagConstraints gbc)
225  {
226    SuperiorObjectClassesChangedListener listener =
227      new SuperiorObjectClassesChangedListener()
228    {
229      /** {@inheritDoc} */
230      @Override
231      public void parentObjectClassesChanged(
232          SuperiorObjectClassesChangedEvent ev)
233      {
234        if (ignoreChangeEvents)
235        {
236          return;
237        }
238        updateAttributesWithParent(true);
239        checkEnableSaveChanges();
240        if (ev.getNewObjectClasses().size() > 1)
241        {
242          lSuperior.setText(
243              INFO_CTRL_PANEL_OBJECTCLASS_PARENTS_LABEL.get().toString());
244        }
245        else
246        {
247          lSuperior.setText(
248              INFO_CTRL_PANEL_OBJECTCLASS_PARENT_LABEL.get().toString());
249        }
250      }
251    };
252    superiors.addParentObjectClassesChangedListener(listener);
253
254    DefaultComboBoxModel model = new DefaultComboBoxModel();
255    for (ObjectClassType t : ObjectClassType.values())
256    {
257      model.addElement(t);
258    }
259    type.setModel(model);
260    type.setSelectedItem(ObjectClassType.STRUCTURAL);
261    SchemaElementComboBoxCellRenderer renderer = new
262    SchemaElementComboBoxCellRenderer(type);
263    type.setRenderer(renderer);
264
265    attributes = new DoubleAddRemovePanel<>(0, AttributeType.class);
266    Comparator<AttributeType> comparator = new Comparator<AttributeType>()
267    {
268      /** {@inheritDoc} */
269      @Override
270      public int compare(AttributeType attr1, AttributeType attr2)
271      {
272        return attr1.getNameOrOID().toLowerCase().compareTo(
273            attr2.getNameOrOID().toLowerCase());
274      }
275    };
276    attributes.getAvailableListModel().setComparator(comparator);
277    attributes.getSelectedListModel1().setComparator(comparator);
278    attributes.getSelectedListModel2().setComparator(comparator);
279
280    gbc.gridy = 0;
281    gbc.gridwidth = 2;
282    addErrorPane(c, gbc);
283    gbc.gridy ++;
284
285    gbc.anchor = GridBagConstraints.WEST;
286    titlePanel.setTitle(INFO_CTRL_PANEL_OBJECTCLASS_DETAILS.get());
287    gbc.fill = GridBagConstraints.NONE;
288    gbc.insets.top = 5;
289    gbc.insets.bottom = 7;
290    c.add(titlePanel, gbc);
291
292    gbc.insets.bottom = 0;
293    gbc.insets.top = 8;
294    gbc.gridy ++;
295    gbc.gridwidth = 1;
296    gbc.fill = GridBagConstraints.HORIZONTAL;
297
298    Component[] basicComps = {name, oid, description, superiors};
299    JLabel[] basicLabels = {lName, lOID, lDescription, lSuperior};
300    JLabel[] basicInlineHelp = new JLabel[] {null, null, null, null};
301    add(basicLabels, basicComps, basicInlineHelp, c, gbc);
302
303    gbc.gridx = 0;
304    gbc.weightx = 0.0;
305    gbc.insets.left = 0;
306    gbc.fill = GridBagConstraints.HORIZONTAL;
307    gbc.anchor = GridBagConstraints.NORTHWEST;
308    c.add(lAttributes, gbc);
309
310    gbc.gridx ++;
311    gbc.fill = GridBagConstraints.BOTH;
312    gbc.weightx = 1.0;
313    gbc.weighty = 1.0;
314    gbc.insets.left = 10;
315    c.add(attributes, gbc);
316    attributes.getAvailableLabel().setText(
317        INFO_CTRL_PANEL_ADDREMOVE_AVAILABLE_ATTRIBUTES.get().toString());
318    attributes.getSelectedLabel1().setText(
319        INFO_CTRL_PANEL_ADDREMOVE_REQUIRED_ATTRIBUTES.get().toString());
320    attributes.getSelectedLabel2().setText(
321        INFO_CTRL_PANEL_ADDREMOVE_OPTIONAL_ATTRIBUTES.get().toString());
322    AttributeTypeCellRenderer listRenderer = new AttributeTypeCellRenderer();
323    attributes.getAvailableList().setCellRenderer(listRenderer);
324    attributes.getSelectedList1().setCellRenderer(listRenderer);
325    attributes.getSelectedList2().setCellRenderer(listRenderer);
326
327    gbc.gridy ++;
328    gbc.weighty = 0.0;
329    gbc.insets.top = 3;
330    JLabel explanation = Utilities.createInlineHelpLabel(
331        INFO_CTRL_PANEL_INHERITED_ATTRIBUTES_HELP.get());
332    gbc.insets.top = 3;
333    c.add(explanation, gbc);
334
335    final BasicExpander expander = new BasicExpander(
336        INFO_CTRL_PANEL_EXTRA_OPTIONS_EXPANDER.get());
337
338    obsolete.setText("Obsolete");
339
340    Component[] comps = {aliases, origin, file, type, obsolete};
341    JLabel[] labels = {lAliases, lOrigin, lFile, lType, null};
342    JLabel[] inlineHelps = {
343        Utilities.createInlineHelpLabel(
344            INFO_CTRL_PANEL_SEPARATED_WITH_COMMAS_HELP.get()), null,
345        Utilities.createInlineHelpLabel(
346            INFO_CTRL_PANEL_SCHEMA_FILE_OBJECTCLASS_HELP.get(File.separator)),
347            null, null};
348    gbc.gridwidth = 2;
349    gbc.gridx = 0;
350    gbc.weighty = 0.0;
351    gbc.insets.left = 0;
352    gbc.gridy ++;
353    c.add(expander, gbc);
354    final JPanel p = new JPanel(new GridBagLayout());
355    gbc.insets.left = 15;
356    gbc.gridy ++;
357    c.add(p, gbc);
358    gbc.gridy ++;
359    p.setOpaque(false);
360
361    GridBagConstraints gbc1 = new GridBagConstraints();
362    gbc1.fill = GridBagConstraints.HORIZONTAL;
363    gbc1.gridy = 0;
364
365    add(labels, comps, inlineHelps, p, gbc1);
366    ChangeListener changeListener = new ChangeListener()
367    {
368      /** {@inheritDoc} */
369      @Override
370      public void stateChanged(ChangeEvent e)
371      {
372        p.setVisible(expander.isSelected());
373      }
374    };
375    expander.addChangeListener(changeListener);
376    expander.setSelected(false);
377    changeListener.stateChanged(null);
378
379    DocumentListener docListener = new DocumentListener()
380    {
381      /** {@inheritDoc} */
382      @Override
383      public void insertUpdate(DocumentEvent ev)
384      {
385        checkEnableSaveChanges();
386      }
387
388      /** {@inheritDoc} */
389      @Override
390      public void removeUpdate(DocumentEvent ev)
391      {
392        checkEnableSaveChanges();
393      }
394
395      /** {@inheritDoc} */
396      @Override
397      public void changedUpdate(DocumentEvent arg0)
398      {
399        checkEnableSaveChanges();
400      }
401    };
402    JTextField[] tfs = {name, description, oid, aliases, origin, file};
403    for (JTextField tf : tfs)
404    {
405      tf.getDocument().addDocumentListener(docListener);
406    }
407
408    ActionListener actionListener = new ActionListener()
409    {
410      @Override
411      public void actionPerformed(ActionEvent ev)
412      {
413        checkEnableSaveChanges();
414      }
415    };
416
417    type.addActionListener(actionListener);
418
419    ListDataListener dataListener = new ListDataListener()
420    {
421      /** {@inheritDoc} */
422      @Override
423      public void contentsChanged(ListDataEvent e)
424      {
425        checkEnableSaveChanges();
426      }
427      /** {@inheritDoc} */
428      @Override
429      public void intervalAdded(ListDataEvent e)
430      {
431        checkEnableSaveChanges();
432      }
433      /** {@inheritDoc} */
434      @Override
435      public void intervalRemoved(ListDataEvent e)
436      {
437        checkEnableSaveChanges();
438      }
439    };
440    SortableListModel<AttributeType> list1 = attributes.getSelectedListModel1();
441    SortableListModel<AttributeType> list2 = attributes.getSelectedListModel2();
442    list1.addListDataListener(dataListener);
443    list2.addListDataListener(dataListener);
444
445    obsolete.addActionListener(actionListener);
446  }
447
448  /**
449   * Updates the contents of the panel with the provided object class.
450   * @param oc the object class.
451   * @param schema the schema.
452   */
453  public void update(ObjectClass oc, Schema schema)
454  {
455    ignoreChangeEvents = true;
456
457    objectClass = oc;
458    if (oc == null || schema == null)
459    {
460      // Ignore: this is called to get an initial panel size.
461      return;
462    }
463    String n = oc.getPrimaryName();
464    if (n == null)
465    {
466      n = NOT_APPLICABLE.toString();
467    }
468    titlePanel.setDetails(LocalizableMessage.raw(n));
469    name.setText(n);
470
471    SortableListModel<AttributeType> modelRequired =
472      attributes.getSelectedListModel1();
473    SortableListModel<AttributeType> modelAvailable =
474      attributes.getSelectedListModel2();
475    SortableListModel<AttributeType> availableModel =
476      attributes.getAvailableListModel();
477    availableModel.addAll(modelRequired.getData());
478    availableModel.addAll(modelAvailable.getData());
479    modelRequired.clear();
480    modelAvailable.clear();
481
482    superiors.setSelectedSuperiors(oc.getSuperiorClasses());
483    superiors.setObjectClassesToExclude(Collections.singleton(oc));
484    if (oc.getSuperiorClasses().size() > 1)
485    {
486      lSuperior.setText(
487          INFO_CTRL_PANEL_OBJECTCLASS_PARENTS_LABEL.get().toString());
488    }
489    else
490    {
491      lSuperior.setText(
492          INFO_CTRL_PANEL_OBJECTCLASS_PARENT_LABEL.get().toString());
493    }
494
495    updateAttributesWithParent(false);
496
497    for (AttributeType attr : oc.getRequiredAttributes())
498    {
499      availableModel.remove(attr);
500      modelRequired.add(attr);
501    }
502    for (AttributeType attr : oc.getOptionalAttributes())
503    {
504      availableModel.remove(attr);
505      modelAvailable.add(attr);
506    }
507    notifyAttributesChanged();
508
509    oid.setText(oc.getOID());
510    n = oc.getDescription();
511    if (n == null)
512    {
513      n = "";
514    }
515    description.setText(n);
516
517    Set<String> aliases = getAliases(oc);
518    lastAliases.clear();
519    lastAliases.addAll(aliases);
520    this.aliases.setText(Utilities.getStringFromCollection(aliases, ", "));
521
522    String sOrigin = Utilities.getOrigin(new SomeSchemaElement(oc));
523    if (sOrigin == null)
524    {
525      sOrigin = "";
526    }
527    origin.setText(sOrigin);
528
529    String sFile = getSchemaFile(oc);
530    if (sFile == null)
531    {
532      sFile = "";
533    }
534    file.setText(sFile);
535
536    type.setSelectedItem(oc.getObjectClassType());
537
538    obsolete.setSelected(oc.isObsolete());
539
540    ocName = objectClass.getNameOrOID();
541    scrollListener.updateBorder();
542    for (JLabel label : labels)
543    {
544      setPrimaryValid(label);
545    }
546    saveChanges.setEnabled(false);
547    ignoreChangeEvents = false;
548  }
549
550  /** {@inheritDoc} */
551  @Override
552  public void configurationChanged(ConfigurationChangeEvent ev)
553  {
554    final ServerDescriptor desc = ev.getNewDescriptor();
555    Schema s = desc.getSchema();
556    final boolean schemaChanged;
557    if (schema != null && s != null)
558    {
559      schemaChanged = !ServerDescriptor.areSchemasEqual(s, schema);
560    }
561    else if (schema == null && s != null)
562    {
563      schemaChanged = true;
564    }
565    else if (s == null && schema != null)
566    {
567      schemaChanged = false;
568    }
569    else
570    {
571      schemaChanged = false;
572    }
573    if (schemaChanged)
574    {
575      schema = s;
576
577      updateErrorPaneIfAuthRequired(desc,
578          isLocal() ?
579        INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_OBJECTCLASS_EDIT.get() :
580      INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname()));
581    }
582    else if (schema == null)
583    {
584      updateErrorPane(errorPane,
585          ERR_CTRL_PANEL_SCHEMA_NOT_FOUND_SUMMARY.get(),
586          ColorAndFontConstants.errorTitleFont,
587          ERR_CTRL_PANEL_SCHEMA_NOT_FOUND_DETAILS.get(),
588          ColorAndFontConstants.defaultFont);
589    }
590    SwingUtilities.invokeLater(new Runnable()
591    {
592      /** {@inheritDoc} */
593      @Override
594      public void run()
595      {
596        delete.setEnabled(!authenticationRequired(desc)
597            && !authenticationRequired(desc)
598            && schema != null);
599        checkEnableSaveChanges();
600        saveChanges.setEnabled(saveChanges.isEnabled() &&
601            !authenticationRequired(desc)
602            && !authenticationRequired(desc)
603            && schema != null);
604        if (schemaChanged && schema != null)
605        {
606          superiors.setSchema(schema);
607          updateAttributes();
608        }
609      }
610    });
611  }
612
613  /** {@inheritDoc} */
614  @Override
615  public boolean mustCheckUnsavedChanges()
616  {
617    return saveChanges.isEnabled();
618  }
619
620  /** {@inheritDoc} */
621  @Override
622  public UnsavedChangesDialog.Result checkUnsavedChanges()
623  {
624    UnsavedChangesDialog.Result result;
625    UnsavedChangesDialog unsavedChangesDlg = new UnsavedChangesDialog(
626          Utilities.getParentDialog(this), getInfo());
627    unsavedChangesDlg.setMessage(INFO_CTRL_PANEL_UNSAVED_CHANGES_SUMMARY.get(),
628        INFO_CTRL_PANEL_UNSAVED_OBJECTCLASS_CHANGES_DETAILS.get(
629           objectClass.getNameOrOID()));
630    Utilities.centerGoldenMean(unsavedChangesDlg,
631          Utilities.getParentDialog(this));
632    unsavedChangesDlg.setVisible(true);
633    result = unsavedChangesDlg.getResult();
634    if (result == UnsavedChangesDialog.Result.SAVE)
635    {
636      ArrayList<LocalizableMessage> errors = new ArrayList<>();
637      saveChanges(true, errors);
638      if (!errors.isEmpty())
639      {
640        result = UnsavedChangesDialog.Result.CANCEL;
641      }
642    }
643
644    return result;
645  }
646
647  /** {@inheritDoc} */
648  @Override
649  public Component getPreferredFocusComponent()
650  {
651    return name;
652  }
653
654  /** {@inheritDoc} */
655  @Override
656  public void okClicked()
657  {
658  }
659
660  private void deleteObjectclass()
661  {
662    ArrayList<LocalizableMessage> errors = new ArrayList<>();
663    ProgressDialog dlg = new ProgressDialog(
664        Utilities.createFrame(),
665        Utilities.getParentDialog(this),
666        INFO_CTRL_PANEL_DELETE_OBJECTCLASS_TITLE.get(), getInfo());
667    LinkedHashSet<ObjectClass> ocsToDelete = new LinkedHashSet<>();
668    ocsToDelete.add(objectClass);
669    LinkedHashSet<AttributeType> attrsToDelete = new LinkedHashSet<>(0);
670
671    DeleteSchemaElementsTask newTask = new DeleteSchemaElementsTask(getInfo(),
672        dlg, ocsToDelete, attrsToDelete);
673    for (Task task : getInfo().getTasks())
674    {
675      task.canLaunch(newTask, errors);
676    }
677    Schema schema = getInfo().getServerDescriptor().getSchema();
678    ArrayList<String> childClasses = new ArrayList<>();
679    if (schema != null)
680    {
681      for (ObjectClass o : schema.getObjectClasses().values())
682      {
683        for (ObjectClass superior : o.getSuperiorClasses())
684        {
685          if (objectClass.equals(superior))
686          {
687            childClasses.add(o.getNameOrOID());
688          }
689        }
690      }
691    }
692    else
693    {
694      errors.add(ERR_CTRL_PANEL_SCHEMA_NOT_FOUND_DETAILS.get());
695    }
696    if (errors.isEmpty())
697    {
698      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
699
700      if (!childClasses.isEmpty())
701      {
702        mb.append(INFO_OBJECTCLASS_IS_SUPERIOR.get(
703            ocName,
704            Utilities.getStringFromCollection(childClasses, ", ")));
705        mb.append("<br>");
706      }
707      LocalizableMessage confirmationMessage =
708        INFO_CTRL_PANEL_CONFIRMATION_DELETE_OBJECTCLASS_DETAILS.get(
709            ocName);
710      mb.append(confirmationMessage);
711      if (displayConfirmationDialog(
712          INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
713          confirmationMessage))
714      {
715        launchOperation(newTask,
716            INFO_CTRL_PANEL_DELETING_OBJECTCLASS_SUMMARY.get(ocName),
717            INFO_CTRL_PANEL_DELETING_OBJECTCLASS_COMPLETE.get(),
718            INFO_CTRL_PANEL_DELETING_OBJECTCLASS_SUCCESSFUL.get(ocName),
719            ERR_CTRL_PANEL_DELETING_OBJECTCLASS_ERROR_SUMMARY.get(),
720            ERR_CTRL_PANEL_DELETING_OBJECTCLASS_ERROR_DETAILS.get(ocName),
721            null,
722            dlg);
723        dlg.setVisible(true);
724      }
725    }
726    else
727    {
728      displayErrorDialog(errors);
729    }
730  }
731
732  private void saveChanges(boolean modal, ArrayList<LocalizableMessage> errors)
733  {
734    for (JLabel label : labels)
735    {
736      setPrimaryValid(label);
737    }
738    String n = getObjectClassName();
739    LocalizableMessageBuilder err = new LocalizableMessageBuilder();
740    if (n.length() == 0)
741    {
742      errors.add(ERR_CTRL_PANEL_OBJECTCLASS_NAME_REQUIRED.get());
743      setPrimaryInvalid(lName);
744    }
745    else if (!n.equalsIgnoreCase(objectClass.getNameOrOID()))
746    {
747      if (!StaticUtils.isValidSchemaElement(n, 0, n.length(), err))
748      {
749        errors.add(ERR_CTRL_PANEL_INVALID_OBJECTCLASS_NAME.get(err));
750        setPrimaryInvalid(lName);
751        err = new LocalizableMessageBuilder();
752      }
753      else
754      {
755        LocalizableMessage elementType = NewAttributePanel.getSchemaElementType(n, schema);
756        if (elementType != null)
757        {
758          errors.add(ERR_CTRL_PANEL_OBJECTCLASS_NAME_ALREADY_IN_USE.get(n, elementType));
759          setPrimaryInvalid(lName);
760        }
761      }
762    }
763    n = oid.getText().trim();
764    if (n.length() > 0 && !n.equalsIgnoreCase(objectClass.getOID()))
765    {
766      if (!StaticUtils.isValidSchemaElement(n, 0, n.length(), err))
767      {
768        errors.add(ERR_CTRL_PANEL_OID_NOT_VALID.get(err));
769        setPrimaryInvalid(lOID);
770        err = new LocalizableMessageBuilder();
771      }
772      else
773      {
774        LocalizableMessage elementType = NewAttributePanel.getSchemaElementType(n, schema);
775        if (elementType != null)
776        {
777          errors.add(ERR_CTRL_PANEL_OID_ALREADY_IN_USE.get(n, elementType));
778          setPrimaryInvalid(lOID);
779        }
780      }
781    }
782
783    Collection<String> aliases = getAliases();
784    Collection<String> oldAliases = getAliases(objectClass);
785
786    if (!aliases.equals(oldAliases))
787    {
788      for (String alias : aliases)
789      {
790        if (alias.trim().length() == 0)
791        {
792          errors.add(ERR_CTRL_PANEL_EMPTY_ALIAS.get());
793          setPrimaryInvalid(lAliases);
794        }
795        else
796        {
797          boolean notPreviouslyDefined = true;
798          for (String oldAlias : oldAliases)
799          {
800            if (oldAlias.equalsIgnoreCase(alias))
801            {
802              notPreviouslyDefined = false;
803              break;
804            }
805          }
806          if (notPreviouslyDefined)
807          {
808            LocalizableMessage elementType =
809              NewAttributePanel.getSchemaElementType(alias, schema);
810            if (elementType != null)
811            {
812              errors.add(ERR_CTRL_PANEL_ALIAS_ALREADY_IN_USE.get(n, elementType));
813              setPrimaryInvalid(lAliases);
814            }
815          }
816        }
817      }
818    }
819
820
821   //validate the superiority.
822    for(ObjectClass superior : getObjectClassSuperiors())
823    {
824      validateSuperiority(superior, errors);
825    }
826    checkCompatibleSuperiors(getObjectClassSuperiors(), getObjectClassType(),
827        errors);
828
829    if (errors.isEmpty())
830    {
831      ProgressDialog dlg = new ProgressDialog(
832          Utilities.createFrame(),
833          Utilities.getParentDialog(this),
834          INFO_CTRL_PANEL_MODIFY_ATTRIBUTE_TITLE.get(), getInfo());
835
836      ModifyObjectClassTask newTask = new ModifyObjectClassTask(getInfo(),
837          dlg, objectClass, getNewObjectClass());
838      for (ConfigurationElementCreatedListener listener :
839        getConfigurationElementCreatedListeners())
840      {
841        newTask.addConfigurationElementCreatedListener(listener);
842      }
843      for (Task task : getInfo().getTasks())
844      {
845        task.canLaunch(newTask, errors);
846      }
847      if (errors.isEmpty())
848      {
849        launchOperation(newTask,
850            INFO_CTRL_PANEL_MODIFYING_OBJECTCLASS_SUMMARY.get(ocName),
851            INFO_CTRL_PANEL_MODIFYING_OBJECTCLASS_COMPLETE.get(),
852            INFO_CTRL_PANEL_MODIFYING_OBJECTCLASS_SUCCESSFUL.get(ocName),
853            ERR_CTRL_PANEL_MODIFYING_OBJECTCLASS_ERROR_SUMMARY.get(),
854            ERR_CTRL_PANEL_MODIFYING_OBJECTCLASS_ERROR_DETAILS.get(ocName),
855            null,
856            dlg);
857        dlg.setVisible(true);
858      }
859    }
860
861    if (!errors.isEmpty())
862    {
863      displayErrorDialog(errors);
864    }
865  }
866
867
868  private void validateSuperiority(ObjectClass superior,
869          ArrayList<LocalizableMessage> errors)
870  {
871    if(superior.getNameOrOID().equalsIgnoreCase(objectClass.getNameOrOID()))
872    {
873      errors.add(ERR_CTRL_PANEL_OBJECTCLASS_CANNOT_BE_ITS_SUPERIOR.get());
874      setPrimaryInvalid(lSuperior);
875      return;
876    }
877    for (ObjectClass obj : superior.getSuperiorClasses())
878    {
879      if (superior.getNameOrOID().equalsIgnoreCase(obj.getNameOrOID()))
880      {
881         errors.add(
882                ERR_CTRL_PANEL_OBJECTCLASS_IS_SUPERIOR_OF_SUPERIOR.get(
883                obj.getNameOrOID()));
884            setPrimaryInvalid(lSuperior);
885        return;
886      }
887      validateSuperiority(obj,errors);
888    }
889  }
890
891  private void checkEnableSaveChanges()
892  {
893    if (!ignoreChangeEvents)
894    {
895      saveChanges.setEnabled(hasChanged());
896    }
897  }
898
899  private boolean hasChanged()
900  {
901    if (objectClass != null)
902    {
903      try
904      {
905        return !objectClass.toString().equals(getNewObjectClass().toString());
906      }
907      catch (Throwable t)
908      {
909        return true;
910      }
911    }
912    return false;
913  }
914
915  private Set<String> getAliases()
916  {
917    Set<String> al = new LinkedHashSet<>();
918    String s = aliases.getText().trim();
919    if (s.length() > 0)
920    {
921      String[] a = s.split(",");
922      for (String alias : a)
923      {
924        al.add(alias.trim());
925      }
926    }
927    return al;
928  }
929
930  private String getObjectClassName()
931  {
932    return name.getText().trim();
933  }
934
935  private String getOID()
936  {
937    String o = oid.getText().trim();
938    if (o.length() == 0)
939    {
940      o = getObjectClassName()+"-oid";
941    }
942    return o;
943  }
944
945  private Map<String, List<String>> getExtraProperties()
946  {
947    Map<String, List<String>> map = new HashMap<>();
948    String f = file.getText().trim();
949    if (f.length() > 0)
950    {
951      map.put(ServerConstants.SCHEMA_PROPERTY_FILENAME, newArrayList(f));
952    }
953    String or = origin.getText().trim();
954    if (or.length() > 0)
955    {
956      map.put(ServerConstants.SCHEMA_PROPERTY_ORIGIN, newArrayList(or));
957    }
958    return map;
959  }
960
961  private ArrayList<String> getAllNames()
962  {
963    ArrayList<String> al = new ArrayList<>();
964    al.add(getObjectClassName());
965    al.addAll(getAliases());
966    return al;
967  }
968
969  private String getDescription()
970  {
971    return description.getText().trim();
972  }
973
974  private Set<ObjectClass> getObjectClassSuperiors()
975  {
976    return superiors.getSelectedSuperiors();
977  }
978
979  private ObjectClassType getObjectClassType()
980  {
981    return (ObjectClassType)type.getSelectedItem();
982  }
983
984  private Set<AttributeType> getRequiredAttributes()
985  {
986    return intersect(attributes.getSelectedListModel1().getData(), inheritedRequiredAttributes);
987  }
988
989  private Set<AttributeType> getOptionalAttributes()
990  {
991    return intersect(attributes.getSelectedListModel2().getData(), inheritedOptionalAttributes);
992  }
993
994  private Set<AttributeType> intersect(Set<AttributeType> set1, Set<AttributeType> set2)
995  {
996    HashSet<AttributeType> attrs = new HashSet<>(set1);
997    attrs.removeAll(set2);
998    return attrs;
999  }
1000
1001  private ObjectClass getNewObjectClass()
1002  {
1003    return new ObjectClass("",
1004        getObjectClassName(),
1005        getAllNames(),
1006        getOID(),
1007        getDescription(),
1008        getObjectClassSuperiors(),
1009        getRequiredAttributes(),
1010        getOptionalAttributes(),
1011        getObjectClassType(),
1012        obsolete.isSelected(),
1013        getExtraProperties());
1014  }
1015
1016  private void updateAttributes()
1017  {
1018    int[][] selected =
1019    {
1020      attributes.getAvailableList().getSelectedIndices(),
1021      attributes.getSelectedList1().getSelectedIndices(),
1022      attributes.getSelectedList2().getSelectedIndices()
1023    };
1024    JList[] lists =
1025    {
1026        attributes.getAvailableList(),
1027        attributes.getSelectedList1(),
1028        attributes.getSelectedList2()
1029    };
1030    attributes.getAvailableListModel().clear();
1031    Collection<AttributeType> allAttrs = schema.getAttributeTypes();
1032    attributes.getAvailableListModel().addAll(allAttrs);
1033
1034
1035    HashSet<AttributeType> toDelete = new HashSet<>();
1036    for (AttributeType attr : attributes.getSelectedListModel1().getData())
1037    {
1038      if (!allAttrs.contains(attr))
1039      {
1040        toDelete.add(attr);
1041      }
1042      else
1043      {
1044        attributes.getAvailableListModel().remove(attr);
1045      }
1046    }
1047    for (AttributeType attr : toDelete)
1048    {
1049      attributes.getSelectedListModel1().remove(attr);
1050    }
1051
1052    toDelete = new HashSet<>();
1053    for (AttributeType attr : attributes.getSelectedListModel2().getData())
1054    {
1055      if (!allAttrs.contains(attr))
1056      {
1057        toDelete.add(attr);
1058      }
1059      else
1060      {
1061        attributes.getAvailableListModel().remove(attr);
1062      }
1063    }
1064    for (AttributeType attr : toDelete)
1065    {
1066      attributes.getSelectedListModel1().remove(attr);
1067    }
1068
1069    int i = 0;
1070    for (int[] sel : selected)
1071    {
1072      if (sel != null)
1073      {
1074        ArrayList<Integer> indexes = new ArrayList<>();
1075        for (int element : sel)
1076        {
1077          if (element < lists[i].getModel().getSize())
1078          {
1079            indexes.add(element);
1080          }
1081        }
1082        int[] newSelection = new int[indexes.size()];
1083        for (int j=0; j<newSelection.length; j++)
1084        {
1085          newSelection[j] = indexes.get(j);
1086        }
1087        lists[i].setSelectedIndices(newSelection);
1088      }
1089      i++;
1090    }
1091  }
1092
1093  private void updateAttributesWithParent(boolean notify)
1094  {
1095 // Remove the previous inherited attributes.
1096    for (AttributeType attr : inheritedRequiredAttributes)
1097    {
1098      attributes.getAvailableListModel().add(attr);
1099      attributes.getSelectedListModel1().remove(attr);
1100    }
1101    for (AttributeType attr : inheritedOptionalAttributes)
1102    {
1103      attributes.getAvailableListModel().add(attr);
1104      attributes.getSelectedListModel2().remove(attr);
1105    }
1106
1107    inheritedOptionalAttributes.clear();
1108    inheritedRequiredAttributes.clear();
1109    for (ObjectClass p : getObjectClassSuperiors())
1110    {
1111      inheritedRequiredAttributes.addAll(p.getRequiredAttributeChain());
1112      inheritedOptionalAttributes.addAll(p.getOptionalAttributeChain());
1113    }
1114    for (AttributeType attr : inheritedRequiredAttributes)
1115    {
1116      attributes.getAvailableListModel().remove(attr);
1117      attributes.getSelectedListModel1().add(attr);
1118    }
1119    for (AttributeType attr : inheritedOptionalAttributes)
1120    {
1121      attributes.getAvailableListModel().remove(attr);
1122      attributes.getSelectedListModel2().add(attr);
1123    }
1124
1125    Collection<AttributeType> unmovableItems = new ArrayList<>(inheritedRequiredAttributes);
1126    unmovableItems.addAll(inheritedOptionalAttributes);
1127    attributes.setUnmovableItems(unmovableItems);
1128
1129    if (notify)
1130    {
1131      notifyAttributesChanged();
1132    }
1133  }
1134
1135  private void notifyAttributesChanged()
1136  {
1137    attributes.getAvailableListModel().fireContentsChanged(
1138        attributes.getAvailableList(), 0,
1139        attributes.getAvailableListModel().getSize() - 1);
1140    attributes.getSelectedListModel1().fireContentsChanged(
1141        attributes.getSelectedList1(), 0,
1142        attributes.getSelectedListModel1().getSize() - 1);
1143    attributes.getSelectedListModel2().fireContentsChanged(
1144        attributes.getSelectedList2(), 0,
1145        attributes.getSelectedListModel2().getSize() - 1);
1146  }
1147
1148
1149  /**
1150   * A renderer for the attribute lists.  The renderer basically marks the
1151   * inherited attributes with an asterisk.
1152   *
1153   */
1154  private class AttributeTypeCellRenderer implements ListCellRenderer
1155  {
1156    private ListCellRenderer defaultRenderer;
1157
1158    /**
1159     * Renderer constructor.
1160     *
1161     */
1162    public AttributeTypeCellRenderer()
1163    {
1164      defaultRenderer = attributes.getAvailableList().getCellRenderer();
1165    }
1166
1167    /** {@inheritDoc} */
1168    @Override
1169    public Component getListCellRendererComponent(JList list, Object value,
1170        int index, boolean isSelected, boolean cellHasFocus)
1171    {
1172      if (value instanceof AttributeType)
1173      {
1174        AttributeType attr = (AttributeType)value;
1175        if (inheritedOptionalAttributes.contains(value) ||
1176            inheritedRequiredAttributes.contains(value))
1177        {
1178          value = attr.getNameOrOID()+ " (*)";
1179        }
1180        else
1181        {
1182          value = attr.getNameOrOID();
1183        }
1184      }
1185      return defaultRenderer.getListCellRendererComponent(list, value, index,
1186          isSelected, cellHasFocus);
1187    }
1188  }
1189}