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 */
017
018package org.opends.guitools.controlpanel.ui;
019
020import static org.opends.messages.AdminToolMessages.*;
021import static org.opends.server.util.StaticUtils.*;
022
023import java.awt.Component;
024import java.awt.Container;
025import java.awt.GridBagConstraints;
026import java.awt.event.KeyAdapter;
027import java.awt.event.KeyEvent;
028import java.awt.event.MouseAdapter;
029import java.awt.event.MouseEvent;
030import java.util.ArrayList;
031import java.util.Comparator;
032import java.util.HashMap;
033import java.util.HashSet;
034import java.util.Map;
035import java.util.Set;
036import java.util.SortedSet;
037import java.util.TreeSet;
038
039import javax.swing.DefaultListModel;
040import javax.swing.JLabel;
041import javax.swing.JList;
042
043import org.forgerock.i18n.LocalizableMessage;
044import org.forgerock.i18n.LocalizableMessageBuilder;
045import org.forgerock.opendj.ldap.schema.AttributeType;
046import org.forgerock.opendj.ldap.schema.ObjectClass;
047import org.opends.guitools.controlpanel.datamodel.SomeSchemaElement;
048import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
049import org.opends.guitools.controlpanel.ui.components.TitlePanel;
050import org.opends.guitools.controlpanel.util.LowerCaseComparator;
051import org.opends.guitools.controlpanel.util.Utilities;
052import org.opends.server.types.Schema;
053
054/** The panel that displays a standard object class definition. */
055public class StandardObjectClassPanel extends SchemaElementPanel
056{
057  private static final long serialVersionUID = 5561268287795223026L;
058  private TitlePanel titlePanel = new TitlePanel(LocalizableMessage.EMPTY, LocalizableMessage.EMPTY);
059
060  private JLabel lParent;
061
062  private JLabel name = Utilities.createDefaultLabel();
063  private JLabel parent = Utilities.createDefaultLabel();
064  private JLabel oid = Utilities.createDefaultLabel();
065  private JLabel origin = Utilities.createDefaultLabel();
066  private JLabel description = Utilities.createDefaultLabel();
067  private JLabel aliases = Utilities.createDefaultLabel();
068  private JLabel type = Utilities.createDefaultLabel();
069  private JList requiredAttributes = new JList(new DefaultListModel());
070  private JList optionalAttributes = new JList(new DefaultListModel());
071
072  private static LocalizableMessage ABSTRACT_VALUE =
073    INFO_CTRL_PANEL_OBJECTCLASS_ABSTRACT_LABEL.get();
074  private static LocalizableMessage STRUCTURAL_VALUE =
075    INFO_CTRL_PANEL_OBJECTCLASS_STRUCTURAL_LABEL.get();
076  private static LocalizableMessage AUXILIARY_VALUE =
077    INFO_CTRL_PANEL_OBJECTCLASS_AUXILIARY_LABEL.get();
078  private static LocalizableMessage OBSOLETE_VALUE =
079    INFO_CTRL_PANEL_OBJECTCLASS_OBSOLETE_LABEL.get();
080
081  private Map<String, AttributeType> hmAttrs = new HashMap<>();
082
083  /** Default constructor of the panel. */
084  public StandardObjectClassPanel()
085  {
086    createLayout();
087  }
088
089  @Override
090  public LocalizableMessage getTitle()
091  {
092    return INFO_CTRL_PANEL_STANDARD_OBJECTCLASS_TITLE.get();
093  }
094
095  @Override
096  public Component getPreferredFocusComponent()
097  {
098    return requiredAttributes;
099  }
100
101  @Override
102  public void configurationChanged(ConfigurationChangeEvent ev)
103  {
104  }
105
106  @Override
107  public void okClicked()
108  {
109  }
110
111  /** Creates the layout of the panel (but the contents are not populated here). */
112  private void createLayout()
113  {
114    createBasicLayout(this, new GridBagConstraints());
115    setBorder(PANEL_BORDER);
116  }
117
118  /**
119   * Creates the basic layout of the panel.
120   * @param c the container where all the components will be layed out.
121   * @param gbc the grid bag constraints.
122   */
123  private void createBasicLayout(Container c, GridBagConstraints gbc)
124  {
125
126    requiredAttributes.setVisibleRowCount(5);
127    optionalAttributes.setVisibleRowCount(9);
128
129    LocalizableMessage[] labels = {
130        INFO_CTRL_PANEL_OBJECTCLASS_NAME_LABEL.get(),
131        INFO_CTRL_PANEL_OBJECTCLASS_PARENT_LABEL.get(),
132        INFO_CTRL_PANEL_OBJECTCLASS_OID_LABEL.get(),
133        INFO_CTRL_PANEL_OBJECTCLASS_ALIASES_LABEL.get(),
134        INFO_CTRL_PANEL_OBJECTCLASS_ORIGIN_LABEL.get(),
135        INFO_CTRL_PANEL_OBJECTCLASS_DESCRIPTION_LABEL.get(),
136        INFO_CTRL_PANEL_OBJECTCLASS_TYPE_LABEL.get()
137    };
138
139    JLabel[] values = {name, parent, oid, aliases, origin, description, type};
140    gbc.gridy = 0;
141    gbc.gridwidth = 2;
142    addErrorPane(c, gbc);
143    gbc.gridy ++;
144    titlePanel.setTitle(INFO_CTRL_PANEL_OBJECTCLASS_DETAILS.get());
145    gbc.fill = GridBagConstraints.NONE;
146    gbc.anchor = GridBagConstraints.WEST;
147    gbc.insets.top = 5;
148    gbc.insets.bottom = 7;
149    c.add(titlePanel, gbc);
150
151    gbc.insets.bottom = 0;
152    gbc.insets.top = 8;
153    gbc.gridy ++;
154    gbc.gridwidth = 1;
155    gbc.fill = GridBagConstraints.HORIZONTAL;
156    for (int i=0; i < labels.length; i++)
157    {
158      gbc.insets.left = 0;
159      gbc.gridx = 0;
160      JLabel l = Utilities.createPrimaryLabel(labels[i]);
161      if (i == 1)
162      {
163        lParent = l;
164      }
165      c.add(l, gbc);
166      gbc.insets.left = 10;
167      gbc.gridx = 1;
168      c.add(values[i], gbc);
169      gbc.gridy ++;
170    }
171    labels = new LocalizableMessage[] {
172        INFO_CTRL_PANEL_REQUIRED_ATTRIBUTES_LABEL.get(),
173        INFO_CTRL_PANEL_OPTIONAL_ATTRIBUTES_LABEL.get()
174        };
175    JList[] lists = {requiredAttributes, optionalAttributes};
176    gbc.anchor = GridBagConstraints.NORTHWEST;
177    for (int i=0; i<2; i++)
178    {
179      gbc.insets.left = 0;
180      gbc.gridx = 0;
181      JLabel l = Utilities.createPrimaryLabel(labels[i]);
182      gbc.weightx = 0.0;
183      gbc.fill = GridBagConstraints.HORIZONTAL;
184      c.add(l, gbc);
185      gbc.insets.left = 10;
186      gbc.gridx = 1;
187      if (i == 0)
188      {
189        gbc.weighty = 0.35;
190      }
191      else
192      {
193        gbc.weighty = 0.65;
194      }
195      gbc.weightx = 1.0;
196      gbc.fill = GridBagConstraints.BOTH;
197      gbc.insets.top = 10;
198      c.add(Utilities.createScrollPane(lists[i]), gbc);
199      gbc.gridy ++;
200      gbc.weighty = 0.0;
201      JLabel explanation = Utilities.createInlineHelpLabel(
202          INFO_CTRL_PANEL_INHERITED_ATTRIBUTES_HELP.get());
203      gbc.insets.top = 3;
204      c.add(explanation, gbc);
205      gbc.gridy ++;
206
207      final JList list = lists[i];
208      MouseAdapter clickListener = new MouseAdapter()
209      {
210        @Override
211        public void mouseClicked(MouseEvent ev)
212        {
213          if (ev.getClickCount() == 1)
214          {
215            attrSelected(list);
216          }
217        }
218      };
219      list.addMouseListener(clickListener);
220
221      KeyAdapter keyListener = new KeyAdapter()
222      {
223        @Override
224        public void keyTyped(KeyEvent ev)
225        {
226          if (ev.getKeyChar() == KeyEvent.VK_SPACE ||
227              ev.getKeyChar() == KeyEvent.VK_ENTER)
228          {
229            attrSelected(list);
230          }
231        }
232      };
233      list.addKeyListener(keyListener);
234    }
235  }
236
237  /**
238   * Returns the message describing the schema element origin (file, RFC, etc.).
239   * @param element the schema element.
240   * @return the message describing the schema element origin (file, RFC, etc.).
241   */
242  static LocalizableMessage getOrigin(SomeSchemaElement element)
243  {
244    LocalizableMessageBuilder returnValue = new LocalizableMessageBuilder();
245    String fileName = element.getSchemaFile();
246    String xOrigin = element.getOrigin();
247    if (xOrigin != null)
248    {
249      returnValue.append(xOrigin);
250      if (fileName != null)
251      {
252        returnValue.append(" -");
253        returnValue.append(
254            INFO_CTRL_PANEL_DEFINED_IN_SCHEMA_FILE.get(fileName));
255      }
256    }
257    else if (fileName != null)
258    {
259      returnValue.append(INFO_CTRL_PANEL_DEFINED_IN_SCHEMA_FILE.get(fileName));
260    }
261    else
262    {
263      returnValue.append(NOT_APPLICABLE);
264    }
265    return returnValue.toMessage();
266  }
267
268  /**
269   * Updates the contents of the panel with the provided object class.
270   * @param oc the object class.
271   * @param schema the schema.
272   */
273  public void update(ObjectClass oc, Schema schema)
274  {
275    if (oc == null || schema == null)
276    {
277      // Ignore: this is called to get an initial panel size.
278      return;
279    }
280    hmAttrs.clear();
281    String n = oc.getNameOrOID();
282    if (n == null)
283    {
284      n = NOT_APPLICABLE.toString();
285    }
286    titlePanel.setDetails(LocalizableMessage.raw(n));
287    name.setText(n);
288    parent.setText(getSuperiorText(oc));
289    oid.setText(oc.getOID());
290    origin.setText(getOrigin(new SomeSchemaElement(oc)).toString());
291    n = oc.getDescription();
292    if (n == null)
293    {
294      n = NOT_APPLICABLE.toString();
295    }
296    description.setText(n);
297    ArrayList<String> otherNames = new ArrayList<>();
298    Iterable<String> ocNames = oc.getNames();
299    String primaryName = oc.getNameOrOID();
300    if (primaryName == null)
301    {
302      primaryName = "";
303    }
304    for (String name : ocNames)
305    {
306      if (!name.equalsIgnoreCase(primaryName))
307      {
308        otherNames.add(toLowerCase(name));
309      }
310    }
311    if (!otherNames.isEmpty())
312    {
313      n = Utilities.getStringFromCollection(otherNames, ", ");
314    }
315    else
316    {
317      n = NOT_APPLICABLE.toString();
318    }
319    aliases.setText(n);
320
321    type.setText(getTypeValue(oc).toString());
322
323    Comparator<String> lowerCaseComparator = new LowerCaseComparator();
324    SortedSet<String> requiredAttrs = new TreeSet<>(lowerCaseComparator);
325    Set<String> inheritedAttrs = new HashSet<>();
326    for (AttributeType attr : oc.getRequiredAttributes())
327    {
328      requiredAttrs.add(attr.getNameOrOID());
329    }
330    Set<ObjectClass> parents = oc.getSuperiorClasses();
331    if (parents != null)
332    {
333      if (parents.size() > 1)
334      {
335        lParent.setText(
336            INFO_CTRL_PANEL_OBJECTCLASS_PARENTS_LABEL.get().toString());
337      }
338      else
339      {
340        lParent.setText(
341            INFO_CTRL_PANEL_OBJECTCLASS_PARENT_LABEL.get().toString());
342      }
343      for (ObjectClass parent : parents)
344      {
345        for (AttributeType attr : parent.getRequiredAttributes())
346        {
347          inheritedAttrs.add(attr.getNameOrOID());
348        }
349      }
350    }
351    else
352    {
353      lParent.setText(
354          INFO_CTRL_PANEL_OBJECTCLASS_PARENT_LABEL.get().toString());
355    }
356
357    DefaultListModel model = (DefaultListModel)requiredAttributes.getModel();
358    model.clear();
359    for (String attr : requiredAttrs)
360    {
361      String v;
362      if (inheritedAttrs.contains(attr))
363      {
364        v = attr+" (*)";
365      }
366      else
367      {
368        v = attr;
369      }
370      model.addElement(v);
371      hmAttrs.put(v, schema.getAttributeType(attr.toLowerCase()));
372    }
373
374    SortedSet<String> optionalAttrs = new TreeSet<>(lowerCaseComparator);
375    inheritedAttrs = new HashSet<>();
376    for (AttributeType attr : oc.getOptionalAttributes())
377    {
378      optionalAttrs.add(attr.getNameOrOID());
379    }
380    if (parents != null)
381    {
382      for (ObjectClass parent : parents)
383      {
384        for (AttributeType attr : parent.getOptionalAttributes())
385        {
386          inheritedAttrs.add(attr.getNameOrOID());
387        }
388      }
389    }
390    model = (DefaultListModel)optionalAttributes.getModel();
391    model.clear();
392    for (String attr : optionalAttrs)
393    {
394      String v;
395      if (inheritedAttrs.contains(attr))
396      {
397        v = attr+" (*)";
398      }
399      else
400      {
401        v = attr;
402      }
403      model.addElement(v);
404      hmAttrs.put(v, schema.getAttributeType(attr.toLowerCase()));
405    }
406  }
407
408  private String getSuperiorText(ObjectClass oc)
409  {
410    String n;
411    Set<ObjectClass> superiors = oc.getSuperiorClasses();
412    if (superiors == null)
413    {
414      n = null;
415    }
416    else
417    {
418      if (superiors.isEmpty())
419      {
420        n = NOT_APPLICABLE.toString();
421      }
422      else if (superiors.size() == 1)
423      {
424        n = superiors.iterator().next().getNameOrOID();
425      }
426      else
427      {
428        SortedSet<String> names = new TreeSet<>();
429        for (ObjectClass superior : superiors)
430        {
431          names.add(superior.getNameOrOID());
432        }
433        n = Utilities.getStringFromCollection(names, ", ");
434      }
435    }
436    if (n == null)
437    {
438      n = NOT_APPLICABLE.toString();
439    }
440    return n;
441  }
442
443  /**
444   * Returns the message describing the object class type (structural, obsolete,
445   * etc.) of a given object class.
446   * @param oc the object class.
447   * @return the message describing the object class type (structural, obsolete,
448   * etc.) of the provided object class.
449   */
450  static LocalizableMessage getTypeValue(ObjectClass oc)
451  {
452    LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
453    switch (oc.getObjectClassType())
454    {
455    case ABSTRACT:
456      mb.append(ABSTRACT_VALUE);
457      break;
458    case STRUCTURAL:
459      mb.append(STRUCTURAL_VALUE);
460      break;
461    case AUXILIARY:
462      mb.append(AUXILIARY_VALUE);
463      break;
464    }
465    if (oc.isObsolete())
466    {
467      if (mb.length() > 0)
468      {
469        mb.append(", ");
470      }
471      mb.append(OBSOLETE_VALUE);
472    }
473    return mb.toMessage();
474  }
475
476  private void attrSelected(JList list)
477  {
478    String o = (String)list.getSelectedValue();
479    if (o != null)
480    {
481      AttributeType attr = hmAttrs.get(o);
482      if (attr != null)
483      {
484        notifySchemaSelectionListeners(attr);
485      }
486    }
487  }
488}