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 2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2015 ForgeRock AS.
026 */
027package org.opends.guitools.controlpanel.ui.components;
028
029import static org.opends.messages.AdminToolMessages.*;
030
031import java.awt.CardLayout;
032import java.awt.GridBagConstraints;
033import java.awt.GridBagLayout;
034import java.awt.event.ActionEvent;
035import java.awt.event.ActionListener;
036import java.awt.event.ItemEvent;
037import java.awt.event.ItemListener;
038import java.util.ArrayList;
039import java.util.Collections;
040import java.util.HashMap;
041import java.util.HashSet;
042import java.util.Set;
043import java.util.SortedSet;
044import java.util.TreeSet;
045
046import javax.swing.Box;
047import javax.swing.DefaultComboBoxModel;
048import javax.swing.JButton;
049import javax.swing.JComboBox;
050import javax.swing.JLabel;
051import javax.swing.JPanel;
052
053import org.opends.guitools.controlpanel.event.SuperiorObjectClassesChangedEvent;
054import org.opends.guitools.controlpanel.event.
055 SuperiorObjectClassesChangedListener;
056import org.opends.guitools.controlpanel.ui.GenericDialog;
057import org.opends.guitools.controlpanel.ui.SelectObjectClassesPanel;
058import org.opends.guitools.controlpanel.ui.renderer.
059 SchemaElementComboBoxCellRenderer;
060import org.opends.guitools.controlpanel.util.LowerCaseComparator;
061import org.opends.guitools.controlpanel.util.Utilities;
062import org.opends.server.types.ObjectClass;
063import org.opends.server.types.Schema;
064
065/**
066 * A panel that can be used to select one (or several) object classes.
067 */
068public class SuperiorObjectClassesEditor extends JPanel
069{
070  private static final long serialVersionUID = 123123973933568L;
071
072  private Set<ObjectClass> toExclude = new HashSet<>();
073  private JComboBox singleSuperior = Utilities.createComboBox();
074  private JLabel multipleSuperiors = Utilities.createDefaultLabel();
075  private JButton bSpecifyMultiple = Utilities.createButton(
076      INFO_CTRL_PANEL_SPECIFY_MULTIPLE_SUPERIORS_LABEL.get());
077  private JButton bUpdateMultiple = Utilities.createButton(
078      INFO_CTRL_PANEL_UPDATE_MULTIPLE_SUPERIORS_LABEL.get());
079
080  private SelectObjectClassesPanel superiorsPanel;
081  private GenericDialog superiorsDialog;
082
083  private String MULTIPLE = "Multiple";
084  private String SINGLE = "Single";
085
086  private CardLayout cardLayout = new CardLayout();
087
088  private boolean isMultiple;
089
090  private Set<ObjectClass> selectedMultipleSuperiors = new HashSet<>();
091  private Set<SuperiorObjectClassesChangedListener> listeners = new HashSet<>();
092
093  private Schema schema;
094
095  /**
096   * Default constructor for this panel.
097   */
098  public SuperiorObjectClassesEditor()
099  {
100    super(new CardLayout());
101    cardLayout = (CardLayout)getLayout();
102    setOpaque(false);
103    createLayout();
104  }
105
106  /**
107   * Constructor for this panel.
108   * @param schema a non {@code null} schema object.
109   */
110  public SuperiorObjectClassesEditor(Schema schema)
111  {
112    this();
113    updateWithSchema(schema);
114  }
115
116  /**
117   * Creates the layout of this panel.
118   */
119  private void createLayout()
120  {
121    bSpecifyMultiple.setToolTipText(
122        INFO_CTRL_PANEL_SPECIFY_MULTIPLE_SUPERIORS_TOOLTIP.get().toString());
123    bSpecifyMultiple.addActionListener(new ActionListener()
124    {
125      public void actionPerformed(ActionEvent ev)
126      {
127        specifyMultipleClicked();
128      }
129    });
130    bUpdateMultiple.setToolTipText(
131        INFO_CTRL_PANEL_UPDATE_MULTIPLE_SUPERIORS_TOOLTIP.get().toString());
132    bUpdateMultiple.addActionListener(new ActionListener()
133    {
134      public void actionPerformed(ActionEvent ev)
135      {
136        updateMultipleClicked();
137      }
138    });
139    SchemaElementComboBoxCellRenderer renderer = new
140    SchemaElementComboBoxCellRenderer(singleSuperior);
141    DefaultComboBoxModel model = new DefaultComboBoxModel();
142    singleSuperior.setModel(model);
143    singleSuperior.setRenderer(renderer);
144    ItemListener itemListener = new ItemListener()
145    {
146      /** {@inheritDoc} */
147      public void itemStateChanged(ItemEvent ev)
148      {
149        notifyListeners();
150      }
151    };
152    singleSuperior.addItemListener(itemListener);
153
154    JPanel singlePanel = new JPanel(new GridBagLayout());
155    singlePanel.setOpaque(false);
156    JPanel multiplePanel = new JPanel(new GridBagLayout());
157    multiplePanel.setOpaque(false);
158    GridBagConstraints gbc = new GridBagConstraints();
159    gbc.gridx = 0;
160    gbc.gridy = 0;
161    gbc.fill = GridBagConstraints.HORIZONTAL;
162    gbc.anchor = GridBagConstraints.WEST;
163    singlePanel.add(singleSuperior, gbc);
164    multiplePanel.add(multipleSuperiors, gbc);
165
166    gbc.gridx ++;
167    gbc.insets.left = 5;
168    gbc.weightx = 0.0;
169    gbc.fill = GridBagConstraints.NONE;
170
171    singlePanel.add(bSpecifyMultiple, gbc);
172    multiplePanel.add(bUpdateMultiple, gbc);
173
174    gbc.gridx ++;
175    gbc.insets.left = 0;
176    gbc.fill = GridBagConstraints.HORIZONTAL;
177    gbc.weightx = 0.1;
178    singlePanel.add(Box.createHorizontalGlue(), gbc);
179    multiplePanel.add(Box.createHorizontalGlue(), gbc);
180
181    add(singlePanel, SINGLE);
182    add(multiplePanel, MULTIPLE);
183
184    Set<ObjectClass> empty = Collections.emptySet();
185    setSelectedSuperiors(empty);
186  }
187
188  /**
189   * Sets the list of object classes that this panel should not display
190   * (mainly used to not display the object class for which we are editing
191   * the superior object classes).
192   * @param toExclude the list of object classes to exclude.
193   */
194  public void setObjectClassesToExclude(Set<ObjectClass> toExclude)
195  {
196    this.toExclude.clear();
197    this.toExclude.addAll(toExclude);
198    updateWithSchema(schema);
199    if (superiorsPanel != null)
200    {
201      superiorsPanel.setObjectClassesToExclude(toExclude);
202    }
203  }
204
205  /**
206   * Returns the list of object classes that this panel will not display.
207   * @return the list of object classes that this panel will not display.
208   */
209  public Set<ObjectClass> getObjectClassToExclude()
210  {
211    return Collections.unmodifiableSet(toExclude);
212  }
213
214  /**
215   * Sets the list of superior object classes that must be displayed by
216   * this panel.
217   * @param objectClasses the list of superior object classes to be displayed.
218   */
219  public void setSelectedSuperiors(Set<ObjectClass> objectClasses)
220  {
221    isMultiple = objectClasses.size() > 1;
222    if (isMultiple)
223    {
224      cardLayout.show(this, MULTIPLE);
225      selectedMultipleSuperiors.clear();
226      selectedMultipleSuperiors.addAll(objectClasses);
227      updateMultipleSuperiorsLabel(selectedMultipleSuperiors);
228    }
229    else
230    {
231      if (objectClasses.size() == 1)
232      {
233        singleSuperior.setSelectedItem(objectClasses.iterator().next());
234      }
235      cardLayout.show(this, SINGLE);
236    }
237  }
238
239  private void updateMultipleSuperiorsLabel(
240      Set<ObjectClass> superiors)
241  {
242    SortedSet<String> orderedOcs = new TreeSet<>(new LowerCaseComparator());
243    for (ObjectClass oc : superiors)
244    {
245      orderedOcs.add(oc.getNameOrOID());
246    }
247    String s = Utilities.getStringFromCollection(orderedOcs, ", ");
248    multipleSuperiors.setText(s);
249  }
250
251  /**
252   * Returns the list of superior object classes displayed by this panel.
253   * @return the list of superior object classes displayed by this panel.
254   */
255  public Set<ObjectClass> getSelectedSuperiors()
256  {
257    if (isMultiple)
258    {
259      return Collections.unmodifiableSet(selectedMultipleSuperiors);
260    }
261
262    ObjectClass oc = (ObjectClass)singleSuperior.getSelectedItem();
263    if (oc != null)
264    {
265      return Collections.singleton(oc);
266    }
267    return Collections.emptySet();
268  }
269
270  /**
271   * Sets the schema to be used by this panel.  This method assumes that it
272   * is being called from the event thread.
273   * @param schema the schema to be used by this panel.
274   */
275  public void setSchema(Schema schema)
276  {
277    updateWithSchema(schema);
278    if (superiorsPanel != null)
279    {
280      superiorsPanel.setSchema(schema);
281    }
282  }
283
284  private void updateWithSchema(Schema schema)
285  {
286    HashMap<String, ObjectClass> objectClassNameMap = new HashMap<>();
287    for (String key : schema.getObjectClasses().keySet())
288    {
289      ObjectClass oc = schema.getObjectClass(key);
290      if (!toExclude.contains(oc))
291      {
292        objectClassNameMap.put(oc.getNameOrOID(), oc);
293      }
294    }
295    SortedSet<String> orderedKeys = new TreeSet<>(new LowerCaseComparator());
296    orderedKeys.addAll(objectClassNameMap.keySet());
297    ArrayList<Object> newParents = new ArrayList<>();
298    for (String key : orderedKeys)
299    {
300      newParents.add(objectClassNameMap.get(key));
301    }
302    Utilities.updateComboBoxModel(newParents,
303        (DefaultComboBoxModel)singleSuperior.getModel());
304
305    if (this.schema == null)
306    {
307      // Select the values.
308      ObjectClass topClass = schema.getObjectClass("top");
309      singleSuperior.setSelectedItem(topClass);
310    }
311    this.schema = schema;
312  }
313
314  /**
315   * Adds a listener that will receive events when a change is made in the
316   * displayed superior object classes.
317   * @param listener the listener to be added.
318   */
319  public void addParentObjectClassesChangedListener(
320      SuperiorObjectClassesChangedListener listener)
321  {
322    listeners.add(listener);
323  }
324
325  /**
326   * Removes the provided listener.
327   * @param listener the listener to be removed.
328   */
329  public void removeParentObjectClassesChangedListener(
330      SuperiorObjectClassesChangedListener listener)
331  {
332    listeners.remove(listener);
333  }
334
335  private void specifyMultipleClicked()
336  {
337    updateMultipleClicked();
338  }
339
340  private void updateMultipleClicked()
341  {
342     Set<ObjectClass> selectedObjectClasses = getSelectedSuperiors();
343
344     // Display the panel with all the stuff.
345     if (superiorsPanel == null)
346     {
347       superiorsPanel = new SelectObjectClassesPanel();
348       superiorsPanel.setSchema(schema);
349       if (!toExclude.isEmpty())
350       {
351         superiorsPanel.setObjectClassesToExclude(toExclude);
352       }
353       superiorsDialog = new GenericDialog(Utilities.getFrame(this),
354           superiorsPanel);
355       Utilities.centerGoldenMean(superiorsDialog,
356           Utilities.getParentDialog(this));
357       superiorsDialog.setModal(true);
358       superiorsDialog.pack();
359     }
360     superiorsPanel.setSelectedObjectClasses(selectedObjectClasses);
361     superiorsDialog.setVisible(true);
362     if (!superiorsPanel.isCanceled())
363     {
364       setSelectedSuperiors(superiorsPanel.getSelectedObjectClasses());
365       notifyListeners();
366     }
367  }
368
369  private void notifyListeners()
370  {
371    SuperiorObjectClassesChangedEvent ev =
372      new SuperiorObjectClassesChangedEvent(this, getSelectedSuperiors());
373    for (SuperiorObjectClassesChangedListener listener : listeners)
374    {
375      listener.parentObjectClassesChanged(ev);
376    }
377  }
378}