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