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