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