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