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