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}