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 2013-2016 ForgeRock AS. 016 */ 017package org.opends.guitools.controlpanel.ui; 018 019import static com.forgerock.opendj.cli.Utils.*; 020 021import static org.opends.messages.AdminToolMessages.*; 022 023import java.awt.Component; 024import java.awt.GridBagConstraints; 025import java.awt.GridBagLayout; 026import java.awt.Insets; 027import java.awt.Point; 028import java.awt.datatransfer.Transferable; 029import java.awt.datatransfer.UnsupportedFlavorException; 030import java.awt.dnd.DnDConstants; 031import java.awt.dnd.DropTarget; 032import java.awt.dnd.DropTargetDragEvent; 033import java.awt.dnd.DropTargetDropEvent; 034import java.awt.dnd.DropTargetEvent; 035import java.awt.dnd.DropTargetListener; 036import java.awt.event.ActionEvent; 037import java.awt.event.ActionListener; 038import java.io.IOException; 039import java.io.StringReader; 040import java.text.ParseException; 041import java.util.ArrayList; 042import java.util.Arrays; 043import java.util.Collection; 044import java.util.HashMap; 045import java.util.HashSet; 046import java.util.LinkedHashMap; 047import java.util.LinkedHashSet; 048import java.util.List; 049import java.util.Map; 050import java.util.Set; 051import java.util.SortedSet; 052import java.util.TreeSet; 053 054import javax.swing.Box; 055import javax.swing.JButton; 056import javax.swing.JCheckBox; 057import javax.swing.JComponent; 058import javax.swing.JLabel; 059import javax.swing.JPanel; 060import javax.swing.JPasswordField; 061import javax.swing.JScrollPane; 062import javax.swing.JTextArea; 063import javax.swing.JTextField; 064import javax.swing.SwingUtilities; 065import javax.swing.border.EmptyBorder; 066import javax.swing.event.DocumentEvent; 067import javax.swing.event.DocumentListener; 068import javax.swing.text.JTextComponent; 069import javax.swing.tree.TreePath; 070 071import org.forgerock.i18n.LocalizableMessage; 072import org.forgerock.i18n.LocalizableMessageBuilder; 073import org.forgerock.opendj.ldap.AVA; 074import org.forgerock.opendj.ldap.ByteString; 075import org.forgerock.opendj.ldap.DN; 076import org.forgerock.opendj.ldap.RDN; 077import org.forgerock.opendj.ldap.schema.AttributeType; 078import org.opends.guitools.controlpanel.datamodel.BinaryValue; 079import org.opends.guitools.controlpanel.datamodel.CheckEntrySyntaxException; 080import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; 081import org.opends.guitools.controlpanel.datamodel.ObjectClassValue; 082import org.opends.guitools.controlpanel.event.ScrollPaneBorderListener; 083import org.opends.guitools.controlpanel.task.OnlineUpdateException; 084import org.opends.guitools.controlpanel.ui.components.BinaryCellPanel; 085import org.opends.guitools.controlpanel.ui.components.ObjectClassCellPanel; 086import org.opends.guitools.controlpanel.ui.nodes.BrowserNodeInfo; 087import org.opends.guitools.controlpanel.ui.nodes.DndBrowserNodes; 088import org.opends.guitools.controlpanel.util.Utilities; 089import org.opends.server.schema.SchemaConstants; 090import org.opends.server.types.Entry; 091import org.opends.server.types.LDIFImportConfig; 092import org.opends.server.types.ObjectClass; 093import org.opends.server.types.OpenDsException; 094import org.opends.server.types.Schema; 095import org.opends.server.util.Base64; 096import org.opends.server.util.LDIFReader; 097import org.opends.server.util.ServerConstants; 098 099/** The panel displaying a simplified view of an entry. */ 100public class SimplifiedViewEntryPanel extends ViewEntryPanel 101{ 102 private static final long serialVersionUID = 2775960608128921072L; 103 private JPanel attributesPanel; 104 private ScrollPaneBorderListener scrollListener; 105 private GenericDialog binaryDlg; 106 private BinaryValuePanel binaryPanel; 107 private GenericDialog editBinaryDlg; 108 private BinaryAttributeEditorPanel editBinaryPanel; 109 private GenericDialog editOcDlg; 110 private ObjectClassEditorPanel editOcPanel; 111 private JLabel requiredLabel; 112 private JCheckBox showOnlyAttrsWithValues; 113 114 private DropTargetListener dropTargetListener; 115 116 private GenericDialog browseEntriesDlg; 117 private LDAPEntrySelectionPanel browseEntriesPanel; 118 119 private Map<String, List<String>> lastUserPasswords = new HashMap<>(); 120 121 private CustomSearchResult searchResult; 122 private boolean isReadOnly; 123 private TreePath treePath; 124 private JScrollPane scrollAttributes; 125 126 private LinkedHashMap<String, List<EditorComponent>> hmEditors = new LinkedHashMap<>(); 127 128 private Set<String> requiredAttrs = new HashSet<>(); 129 private Map<String, JComponent> hmLabels = new HashMap<>(); 130 private Map<String, String> hmDisplayedNames = new HashMap<>(); 131 private Map<String, JComponent> hmComponents = new HashMap<>(); 132 133 private final String CONFIRM_PASSWORD = "confirm password"; 134 135 /** Map containing as key the attribute name and as value a localizable message. */ 136 static Map<String, LocalizableMessage> hmFriendlyAttrNames = new HashMap<>(); 137 /** 138 * Map containing as key an object class and as value the preferred naming 139 * attribute for the objectclass. 140 */ 141 static Map<String, String> hmNameAttrNames = new HashMap<>(); 142 static Map<String, String[]> hmOrdereredAttrNames = new HashMap<>(); 143 static 144 { 145 hmFriendlyAttrNames.put(ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME, 146 INFO_CTRL_PANEL_OBJECTCLASS_FRIENDLY_NAME.get()); 147 hmFriendlyAttrNames.put(ServerConstants.ATTR_COMMON_NAME, 148 INFO_CTRL_PANEL_CN_FRIENDLY_NAME.get()); 149 hmFriendlyAttrNames.put("givenname", 150 INFO_CTRL_PANEL_GIVENNAME_FRIENDLY_NAME.get()); 151 hmFriendlyAttrNames.put("sn", 152 INFO_CTRL_PANEL_SN_FRIENDLY_NAME.get()); 153 hmFriendlyAttrNames.put("uid", 154 INFO_CTRL_PANEL_UID_FRIENDLY_NAME.get()); 155 hmFriendlyAttrNames.put("employeenumber", 156 INFO_CTRL_PANEL_EMPLOYEENUMBER_FRIENDLY_NAME.get()); 157 hmFriendlyAttrNames.put("userpassword", 158 INFO_CTRL_PANEL_USERPASSWORD_FRIENDLY_NAME.get()); 159 hmFriendlyAttrNames.put("authpassword", 160 INFO_CTRL_PANEL_AUTHPASSWORD_FRIENDLY_NAME.get()); 161 hmFriendlyAttrNames.put("mail", 162 INFO_CTRL_PANEL_MAIL_FRIENDLY_NAME.get()); 163 hmFriendlyAttrNames.put("street", 164 INFO_CTRL_PANEL_STREET_FRIENDLY_NAME.get()); 165 hmFriendlyAttrNames.put("l", 166 INFO_CTRL_PANEL_L_FRIENDLY_NAME.get()); 167 hmFriendlyAttrNames.put("st", 168 INFO_CTRL_PANEL_ST_FRIENDLY_NAME.get()); 169 hmFriendlyAttrNames.put("postalcode", 170 INFO_CTRL_PANEL_POSTALCODE_FRIENDLY_NAME.get()); 171 hmFriendlyAttrNames.put("mobile", 172 INFO_CTRL_PANEL_MOBILE_FRIENDLY_NAME.get()); 173 hmFriendlyAttrNames.put("homephone", 174 INFO_CTRL_PANEL_HOMEPHONE_FRIENDLY_NAME.get()); 175 hmFriendlyAttrNames.put("telephonenumber", 176 INFO_CTRL_PANEL_TELEPHONENUMBER_FRIENDLY_NAME.get()); 177 hmFriendlyAttrNames.put("pager", 178 INFO_CTRL_PANEL_PAGER_FRIENDLY_NAME.get()); 179 hmFriendlyAttrNames.put("facsimiletelephonenumber", 180 INFO_CTRL_PANEL_FACSIMILETELEPHONENUMBER_FRIENDLY_NAME.get()); 181 hmFriendlyAttrNames.put("description", 182 INFO_CTRL_PANEL_DESCRIPTION_FRIENDLY_NAME.get()); 183 hmFriendlyAttrNames.put("postaladdress", 184 INFO_CTRL_PANEL_POSTALADDRESS_FRIENDLY_NAME.get()); 185 hmFriendlyAttrNames.put(ServerConstants.ATTR_UNIQUE_MEMBER_LC, 186 INFO_CTRL_PANEL_UNIQUEMEMBER_FRIENDLY_NAME.get()); 187 hmFriendlyAttrNames.put(ServerConstants.ATTR_MEMBER, 188 INFO_CTRL_PANEL_MEMBER_FRIENDLY_NAME.get()); 189 hmFriendlyAttrNames.put(ServerConstants.ATTR_MEMBER_URL_LC, 190 INFO_CTRL_PANEL_MEMBERURL_FRIENDLY_NAME.get()); 191 hmFriendlyAttrNames.put(ServerConstants.ATTR_C, 192 INFO_CTRL_PANEL_C_FRIENDLY_NAME.get()); 193 hmFriendlyAttrNames.put("ds-target-group-dn", 194 INFO_CTRL_PANEL_DS_TARGET_GROUP_DN_FRIENDLY_NAME.get()); 195 hmFriendlyAttrNames.put("usercertificate", 196 INFO_CTRL_PANEL_USERCERTIFICATE_FRIENDLY_NAME.get()); 197 hmFriendlyAttrNames.put("jpegphoto", 198 INFO_CTRL_PANEL_JPEGPHOTO_FRIENDLY_NAME.get()); 199 hmFriendlyAttrNames.put(ServerConstants.ATTR_SUPPORTED_AUTH_PW_SCHEMES_LC, 200 INFO_CTRL_PANEL_SUPPORTEDPWDSCHEMES_FRIENDLY_NAME.get()); 201 hmFriendlyAttrNames.put(ServerConstants.ATTR_SUPPORTED_CONTROL_LC, 202 INFO_CTRL_PANEL_SUPPORTEDCONTROLS_FRIENDLY_NAME.get()); 203 hmFriendlyAttrNames.put(ServerConstants.ATTR_SUPPORTED_LDAP_VERSION_LC, 204 INFO_CTRL_PANEL_SUPPORTEDLDAPVERSIONS_FRIENDLY_NAME.get()); 205 hmFriendlyAttrNames.put(ServerConstants.ATTR_SUPPORTED_CONTROL_LC, 206 INFO_CTRL_PANEL_SUPPORTEDCONTROLS_FRIENDLY_NAME.get()); 207 hmFriendlyAttrNames.put(ServerConstants.ATTR_SUPPORTED_EXTENSION_LC, 208 INFO_CTRL_PANEL_SUPPORTEDEXTENSIONS_FRIENDLY_NAME.get()); 209 hmFriendlyAttrNames.put(ServerConstants.ATTR_SUPPORTED_FEATURE_LC, 210 INFO_CTRL_PANEL_SUPPORTEDFEATURES_FRIENDLY_NAME.get()); 211 hmFriendlyAttrNames.put(ServerConstants.ATTR_VENDOR_NAME_LC, 212 INFO_CTRL_PANEL_VENDORNAME_FRIENDLY_NAME.get()); 213 hmFriendlyAttrNames.put(ServerConstants.ATTR_VENDOR_VERSION_LC, 214 INFO_CTRL_PANEL_VENDORVERSION_FRIENDLY_NAME.get()); 215 hmFriendlyAttrNames.put(ServerConstants.ATTR_NAMING_CONTEXTS_LC, 216 INFO_CTRL_PANEL_NAMINGCONTEXTS_FRIENDLY_NAME.get()); 217 hmFriendlyAttrNames.put(ServerConstants.ATTR_PRIVATE_NAMING_CONTEXTS, 218 INFO_CTRL_PANEL_PRIVATENAMINGCONTEXTS_FRIENDLY_NAME.get()); 219 220 hmNameAttrNames.put("organizationalunit", ServerConstants.ATTR_OU); 221 hmNameAttrNames.put("domain", ServerConstants.ATTR_DC); 222 hmNameAttrNames.put("organization", ServerConstants.ATTR_O); 223 hmNameAttrNames.put(ServerConstants.OC_GROUP_OF_URLS_LC, 224 ServerConstants.ATTR_COMMON_NAME); 225 hmNameAttrNames.put(ServerConstants.OC_GROUP_OF_NAMES_LC, 226 ServerConstants.ATTR_COMMON_NAME); 227 228 hmOrdereredAttrNames.put("person", 229 new String[]{"givenname", "sn", ServerConstants.ATTR_COMMON_NAME, "uid", 230 "userpassword", "mail", "telephonenumber", "facsimiletelephonenumber", 231 "employeenumber", "street", "l", "st", "postalcode", "mobile", 232 "homephone", "pager", "description", "postaladdress"}); 233 hmOrdereredAttrNames.put(ServerConstants.OC_GROUP_OF_NAMES_LC, 234 new String[]{"cn", "description", 235 ServerConstants.ATTR_UNIQUE_MEMBER_LC, "ds-target-group-dn"}); 236 hmOrdereredAttrNames.put(ServerConstants.OC_GROUP_OF_URLS_LC, 237 new String[]{"cn", "description", ServerConstants.ATTR_MEMBER_URL_LC}); 238 hmOrdereredAttrNames.put("organizationalunit", 239 new String[]{"ou", "description", "postalAddress", "telephonenumber", 240 "facsimiletelephonenumber"}); 241 hmOrdereredAttrNames.put("organization", new String[]{"o", "description"}); 242 hmOrdereredAttrNames.put("domain", new String[]{"dc", "description"}); 243 } 244 245 private LocalizableMessage NAME = INFO_CTRL_PANEL_NAME_LABEL.get(); 246 247 /** Default constructor. */ 248 public SimplifiedViewEntryPanel() 249 { 250 super(); 251 createLayout(); 252 } 253 254 /** {@inheritDoc} */ 255 public Component getPreferredFocusComponent() 256 { 257 return null; 258 } 259 260 /** {@inheritDoc} */ 261 public boolean requiresBorder() 262 { 263 return false; 264 } 265 266 /** 267 * Creates the layout of the panel (but the contents are not populated here). 268 */ 269 private void createLayout() 270 { 271 dropTargetListener = new DropTargetListener() 272 { 273 /** {@inheritDoc} */ 274 public void dragEnter(DropTargetDragEvent e) 275 { 276 } 277 278 /** {@inheritDoc} */ 279 public void dragExit(DropTargetEvent e) 280 { 281 } 282 283 /** {@inheritDoc} */ 284 public void dragOver(DropTargetDragEvent e) 285 { 286 } 287 288 /** {@inheritDoc} */ 289 public void dropActionChanged(DropTargetDragEvent e) 290 { 291 } 292 293 /** {@inheritDoc} */ 294 public void drop(DropTargetDropEvent e) 295 { 296 try { 297 Transferable tr = e.getTransferable(); 298 299 //flavor not supported, reject drop 300 if (!tr.isDataFlavorSupported(DndBrowserNodes.INFO_FLAVOR)) 301 { 302 e.rejectDrop(); 303 } 304 305 //cast into appropriate data type 306 DndBrowserNodes nodes = 307 (DndBrowserNodes) tr.getTransferData(DndBrowserNodes.INFO_FLAVOR); 308 309 Component comp = e.getDropTargetContext().getComponent(); 310 if (comp instanceof JTextArea) 311 { 312 JTextArea ta = (JTextArea)comp; 313 StringBuilder sb = new StringBuilder(); 314 sb.append(ta.getText()); 315 for (BrowserNodeInfo node : nodes.getNodes()) 316 { 317 if (sb.length() > 0) 318 { 319 sb.append("\n"); 320 } 321 sb.append(node.getNode().getDN()); 322 } 323 ta.setText(sb.toString()); 324 ta.setCaretPosition(sb.length()); 325 } 326 else if (comp instanceof JTextField) 327 { 328 JTextField tf = (JTextField)comp; 329 if (nodes.getNodes().length > 0) 330 { 331 String dn = nodes.getNodes()[0].getNode().getDN(); 332 tf.setText(dn); 333 tf.setCaretPosition(dn.length()); 334 } 335 } 336 e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); 337 e.getDropTargetContext().dropComplete(true); 338 } 339 catch (IOException | UnsupportedFlavorException io) 340 { 341 e.rejectDrop(); 342 } 343 } 344 }; 345 346 GridBagConstraints gbc = new GridBagConstraints(); 347 gbc.gridx = 0; 348 gbc.gridy = 0; 349 gbc.gridwidth = 2; 350 gbc.fill = GridBagConstraints.NONE; 351 gbc.anchor = GridBagConstraints.WEST; 352 gbc.weightx = 1.0; 353 gbc.insets = new Insets(10, 10, 0, 10); 354 355 addTitlePanel(this, gbc); 356 357 gbc.gridy ++; 358 gbc.insets = new Insets(5, 10, 5, 10); 359 360 gbc.gridwidth = 1; 361 showOnlyAttrsWithValues = 362 Utilities.createCheckBox( 363 INFO_CTRL_PANEL_SHOW_ATTRS_WITH_VALUES_LABEL.get()); 364 showOnlyAttrsWithValues.setSelected(displayOnlyWithAttrs); 365 showOnlyAttrsWithValues.addActionListener(new ActionListener() 366 { 367 /** {@inheritDoc} */ 368 public void actionPerformed(ActionEvent ev) 369 { 370 updateAttributeVisibility(!showOnlyAttrsWithValues.isSelected()); 371 displayOnlyWithAttrs = showOnlyAttrsWithValues.isSelected(); 372 } 373 }); 374 gbc.weightx = 0.0; 375 gbc.anchor = GridBagConstraints.WEST; 376 add(showOnlyAttrsWithValues, gbc); 377 gbc.gridx ++; 378 gbc.anchor = GridBagConstraints.EAST; 379 gbc.fill = GridBagConstraints.NONE; 380 requiredLabel = createRequiredLabel(); 381 add(requiredLabel, gbc); 382 gbc.insets = new Insets(0, 0, 0, 0); 383 add(Box.createVerticalStrut(10), gbc); 384 385 showOnlyAttrsWithValues.setFont(requiredLabel.getFont()); 386 387 attributesPanel = new JPanel(new GridBagLayout()); 388 attributesPanel.setOpaque(false); 389 attributesPanel.setBorder(new EmptyBorder(5, 10, 5, 10)); 390 gbc.gridx = 0; 391 gbc.weightx = 1.0; 392 gbc.weighty = 1.0; 393 gbc.gridwidth = 2; 394 gbc.gridy ++; 395 gbc.fill = GridBagConstraints.BOTH; 396 scrollAttributes = Utilities.createBorderLessScrollBar(attributesPanel); 397 scrollListener = ScrollPaneBorderListener.createBottomAndTopBorderListener( 398 scrollAttributes); 399 gbc.insets = new Insets(0, 0, 0, 0); 400 add(scrollAttributes, gbc); 401 } 402 403 /** {@inheritDoc} */ 404 public void update(CustomSearchResult sr, boolean isReadOnly, TreePath path) 405 { 406 boolean sameEntry = false; 407 if (searchResult != null && sr != null) 408 { 409 sameEntry = searchResult.getDN().equals(sr.getDN()); 410 } 411 final Point p = sameEntry ? 412 scrollAttributes.getViewport().getViewPosition() : new Point(0, 0); 413 searchResult = sr; 414 this.isReadOnly = isReadOnly; 415 this.treePath = path; 416 417 updateTitle(sr, path); 418 419 requiredLabel.setVisible(!isReadOnly); 420 showOnlyAttrsWithValues.setVisible(!isReadOnly); 421 422 attributesPanel.removeAll(); 423 424 GridBagConstraints gbc = new GridBagConstraints(); 425 gbc.fill = GridBagConstraints.HORIZONTAL; 426 427 lastUserPasswords.clear(); 428 hmEditors.clear(); 429 430 hmLabels.clear(); 431 hmDisplayedNames.clear(); 432 hmComponents.clear(); 433 requiredAttrs.clear(); 434 435 436 // Build the attributes panel. 437 Collection<String> sortedAttributes = getSortedAttributes(sr, isReadOnly); 438 if (isReadOnly) 439 { 440 for (String attr : sortedAttributes) 441 { 442 JLabel label = getLabelForAttribute(attr, sr); 443 List<Object> values = sr.getAttributeValues(attr); 444 JComponent comp = getReadOnlyComponent(attr, values); 445 gbc.weightx = 0.0; 446 if (values.size() > 1) 447 { 448 gbc.anchor = GridBagConstraints.NORTHWEST; 449 } 450 else 451 { 452 gbc.anchor = GridBagConstraints.WEST; 453 if (values.size() == 1) 454 { 455 Object v = values.get(0); 456 if (v instanceof String 457 && ((String) v).contains("\n")) 458 { 459 gbc.anchor = GridBagConstraints.NORTHWEST; 460 } 461 } 462 } 463 gbc.insets.left = 0; 464 gbc.gridwidth = GridBagConstraints.RELATIVE; 465 attributesPanel.add(label, gbc); 466 gbc.insets.left = 10; 467 gbc.weightx = 1.0; 468 gbc.gridwidth = GridBagConstraints.REMAINDER; 469 attributesPanel.add(comp, gbc); 470 gbc.insets.top = 10; 471 } 472 } 473 else 474 { 475 for (String attr : sortedAttributes) 476 { 477 JLabel label = getLabelForAttribute(attr, sr); 478 if (isRequired(attr, sr)) 479 { 480 Utilities.setRequiredIcon(label); 481 requiredAttrs.add(attr.toLowerCase()); 482 } 483 List<Object> values = sr.getAttributeValues(attr); 484 if (values.isEmpty()) 485 { 486 values = new ArrayList<>(1); 487 if (isBinary(attr)) 488 { 489 values.add(new byte[]{}); 490 } 491 else 492 { 493 values.add(""); 494 } 495 } 496 497 if (isPassword(attr)) 498 { 499 List<String> pwds = new ArrayList<>(); 500 for (Object o : values) 501 { 502 pwds.add(getPasswordStringValue(o)); 503 } 504 lastUserPasswords.put(attr.toLowerCase(), pwds); 505 } 506 507 JComponent comp = getReadWriteComponent(attr, values); 508 509 gbc.weightx = 0.0; 510 if (attr.equalsIgnoreCase( 511 ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME)) 512 { 513 int nOcs = 0; 514 for (Object o : values) 515 { 516 if (!"top".equals(o)) 517 { 518 nOcs ++; 519 } 520 } 521 if (nOcs > 1) 522 { 523 gbc.anchor = GridBagConstraints.NORTHWEST; 524 } 525 else 526 { 527 gbc.anchor = GridBagConstraints.WEST; 528 } 529 } 530 else if (isSingleValue(attr)) 531 { 532 gbc.anchor = GridBagConstraints.WEST; 533 } 534 else if (values.size() <= 1 && 535 (hasBinaryValue(values) || isBinary(attr))) 536 { 537 gbc.anchor = GridBagConstraints.WEST; 538 } 539 else 540 { 541 gbc.anchor = GridBagConstraints.NORTHWEST; 542 } 543 gbc.insets.left = 0; 544 gbc.gridwidth = GridBagConstraints.RELATIVE; 545 attributesPanel.add(label, gbc); 546 gbc.insets.left = 10; 547 gbc.weightx = 1.0; 548 gbc.gridwidth = GridBagConstraints.REMAINDER; 549 attributesPanel.add(comp, gbc); 550 gbc.insets.top = 10; 551 hmLabels.put(attr.toLowerCase(), label); 552 hmComponents.put(attr.toLowerCase(), comp); 553 554 if (isPassword(attr)) 555 { 556 label = Utilities.createPrimaryLabel( 557 INFO_CTRL_PANEL_PASSWORD_CONFIRM_LABEL.get()); 558 String key = getConfirmPasswordKey(attr); 559 comp = getReadWriteComponent(key, values); 560 561 hmLabels.put(key, label); 562 hmComponents.put(key, comp); 563 564 gbc.weightx = 0.0; 565 if (isSingleValue(attr)) 566 { 567 gbc.anchor = GridBagConstraints.WEST; 568 } 569 else 570 { 571 gbc.anchor = GridBagConstraints.NORTHWEST; 572 } 573 gbc.insets.left = 0; 574 gbc.gridwidth = GridBagConstraints.RELATIVE; 575 attributesPanel.add(label, gbc); 576 gbc.insets.left = 10; 577 gbc.weightx = 1.0; 578 gbc.gridwidth = GridBagConstraints.REMAINDER; 579 attributesPanel.add(comp, gbc); 580 gbc.insets.top = 10; 581 } 582 } 583 } 584 gbc.weighty = 1.0; 585 gbc.gridwidth = GridBagConstraints.REMAINDER; 586 gbc.fill = GridBagConstraints.VERTICAL; 587 gbc.insets = new Insets(0, 0, 0, 0); 588 attributesPanel.add(Box.createVerticalGlue(), gbc); 589 scrollListener.updateBorder(); 590 591 if (showOnlyAttrsWithValues.isSelected()) 592 { 593 updateAttributeVisibility(false); 594 } 595 else if (isVisible()) 596 { 597 repaint(); 598 } 599 600 SwingUtilities.invokeLater(new Runnable() 601 { 602 /** {@inheritDoc} */ 603 public void run() 604 { 605 if (p != null && scrollAttributes.getViewport().contains(p)) 606 { 607 scrollAttributes.getViewport().setViewPosition(p); 608 } 609 ignoreEntryChangeEvents = false; 610 } 611 }); 612 } 613 614 private JLabel getLabelForAttribute(String attrName, CustomSearchResult sr) 615 { 616 LocalizableMessageBuilder l = new LocalizableMessageBuilder(); 617 int index = attrName.indexOf(";"); 618 String basicAttrName; 619 String subType; 620 if (index == -1) 621 { 622 basicAttrName = attrName; 623 subType = null; 624 } 625 else 626 { 627 basicAttrName = attrName.substring(0, index); 628 subType = attrName.substring(index + 1); 629 } 630 if ("binary".equalsIgnoreCase(subType)) 631 { 632 // TODO: use message 633 subType = "binary"; 634 } 635 boolean isNameAttribute = isAttrName(basicAttrName, sr); 636 if (isNameAttribute) 637 { 638 if (subType != null) 639 { 640 l.append(NAME).append(" (").append(subType).append(")"); 641 } 642 else 643 { 644 l.append(NAME); 645 } 646 } 647 else 648 { 649 LocalizableMessage friendly = hmFriendlyAttrNames.get(basicAttrName.toLowerCase()); 650 if (friendly == null) 651 { 652 l.append(attrName); 653 } 654 else 655 { 656 l.append(friendly); 657 if (subType != null) 658 { 659 l.append(" (").append(subType).append(")"); 660 } 661 } 662 } 663 hmDisplayedNames.put(attrName.toLowerCase(), l.toString()); 664 l.append(":"); 665 return Utilities.createPrimaryLabel(l.toMessage()); 666 } 667 668 private Collection<String> getSortedAttributes(CustomSearchResult sr, 669 boolean isReadOnly) 670 { 671 LinkedHashSet<String> attrNames = new LinkedHashSet<>(); 672 673// Get all attributes that the entry can have 674 Set<String> attributes = new LinkedHashSet<>(); 675 ArrayList<String> entryAttrs = new ArrayList<>(sr.getAttributeNames()); 676 ArrayList<String> attrsWithNoOptions = new ArrayList<>(); 677 for (String attr : entryAttrs) 678 { 679 attrsWithNoOptions.add( 680 Utilities.getAttributeNameWithoutOptions(attr).toLowerCase()); 681 } 682 683 List<Object> values = 684 sr.getAttributeValues(ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME); 685 686 // Put first the attributes associated with the objectclass in 687 // hmOrderedAttrNames 688 for (Object o : values) 689 { 690 String ocName = (String)o; 691 String[] attrs = hmOrdereredAttrNames.get(ocName.toLowerCase()); 692 if (attrs != null) 693 { 694 for (String attr : attrs) 695 { 696 int index = attrsWithNoOptions.indexOf(attr.toLowerCase()); 697 if (index != -1) 698 { 699 attrNames.add(entryAttrs.get(index)); 700 } 701 else 702 { 703 attrNames.add(attr); 704 } 705 } 706 } 707 } 708 // Handle the root entry separately: most of its attributes are operational 709 // so we filter a list of harcoded attributes. 710 boolean isRootEntry = sr.getDN().equals(""); 711 Schema schema = getInfo().getServerDescriptor().getSchema(); 712 if (isRootEntry) 713 { 714 List<String> attrsNotToAdd = Arrays.asList("entryuuid", "hassubordinates", 715 "numsubordinates", "subschemasubentry", "entrydn", "hassubordinates"); 716 for (String attr : sr.getAttributeNames()) 717 { 718 if (!find(attrNames, attr) && !find(attrsNotToAdd, attr)) 719 { 720 attrNames.add(attr); 721 } 722 } 723 } 724 else 725 { 726 // Try to get the attributes from the schema: first display the required 727 // attributes with a friendly name (in alphabetical order), then (in 728 // alphabetical order) the attributes with no friendly name. Finally 729 // do the same with the other attributes. 730 731 SortedSet<String> requiredAttributes = new TreeSet<>(); 732 SortedSet<String> allowedAttributes = new TreeSet<>(); 733 734 if (schema != null) 735 { 736 List<Object> ocs = sr.getAttributeValues( 737 ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME); 738 for (Object o : ocs) 739 { 740 String oc = (String)o; 741 ObjectClass objectClass = schema.getObjectClass(oc.toLowerCase()); 742 if (objectClass != null) 743 { 744 for (AttributeType attr : objectClass.getRequiredAttributeChain()) 745 { 746 requiredAttributes.add(attr.getNameOrOID()); 747 } 748 for (AttributeType attr : objectClass.getOptionalAttributeChain()) 749 { 750 allowedAttributes.add(attr.getNameOrOID()); 751 } 752 } 753 } 754 } 755 // Now try to put first the attributes for which we have a friendly 756 // name (the most common ones). 757 updateAttributes(attributes, requiredAttributes, entryAttrs, 758 attrsWithNoOptions, true); 759 updateAttributes(attributes, requiredAttributes, entryAttrs, 760 attrsWithNoOptions, false); 761 updateAttributes(attributes, allowedAttributes, entryAttrs, 762 attrsWithNoOptions, true); 763 updateAttributes(attributes, allowedAttributes, entryAttrs, 764 attrsWithNoOptions, false); 765 766 767 attributes.addAll(entryAttrs); 768 attributes.add("aci"); 769 770 // In read-only mode display only the attributes with values 771 if (isReadOnly) 772 { 773 attributes.retainAll(entryAttrs); 774 } 775 776 for (String attr : attributes) 777 { 778 boolean canAdd = isEditable(attr, schema); 779 if (canAdd && !find(attrNames, attr)) 780 { 781 attrNames.add(attr); 782 } 783 } 784 } 785 return attrNames; 786 } 787 788 private boolean find(Collection<String> attrNames, String attrNameToFind) 789 { 790 for (String attrName : attrNames) 791 { 792 if (attrName.equalsIgnoreCase(attrNameToFind)) 793 { 794 return true; 795 } 796 } 797 return false; 798 } 799 800 private void updateAttributes( 801 Collection<String> attributes, 802 Set<String> newAttributes, 803 ArrayList<String> entryAttrs, 804 ArrayList<String> attrsWithNoOptions, 805 boolean addIfFriendlyName) 806 { 807 for (String attr : newAttributes) 808 { 809 String attrLc = attr.toLowerCase(); 810 boolean hasFriendlyName = hmFriendlyAttrNames.get(attrLc) != null; 811 if (hasFriendlyName == addIfFriendlyName) 812 { 813 int index = attrsWithNoOptions.indexOf(attrLc); 814 if (index != -1) 815 { 816 attributes.add(entryAttrs.get(index)); 817 } 818 else 819 { 820 if (!hasCertificateSyntax(attr, 821 getInfo().getServerDescriptor().getSchema())) 822 { 823 attributes.add(attr); 824 } 825 else 826 { 827 attributes.add(attr+";binary"); 828 } 829 } 830 } 831 } 832 } 833 834 private JComponent getReadOnlyComponent(final String attrName, 835 List<Object> values) 836 { 837// GridLayout is used to avoid the 512 limit of GridBagLayout 838 JPanel panel = new JPanel(new GridBagLayout()); 839 panel.setOpaque(false); 840 GridBagConstraints gbc = new GridBagConstraints(); 841 gbc.gridy = 0; 842 843 boolean isBinary = hasBinaryValue(values); 844 for (Object o : values) 845 { 846 gbc.fill = GridBagConstraints.HORIZONTAL; 847 gbc.weightx = 1.0; 848 gbc.gridx = 0; 849 850 if (attrName.equalsIgnoreCase( 851 ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME)) 852 { 853 ObjectClassCellPanel ocPanel = new ObjectClassCellPanel(); 854 Schema schema = getInfo().getServerDescriptor().getSchema(); 855 if (schema != null) 856 { 857 ObjectClassValue ocDescriptor = getObjectClassDescriptor(values, 858 schema); 859 ocPanel.setValue(ocDescriptor); 860 } 861 ocPanel.setEditButtonVisible(false); 862 panel.add(ocPanel, gbc); 863 break; 864 } 865 else if (Utilities.mustObfuscate(attrName, 866 getInfo().getServerDescriptor().getSchema())) 867 { 868 panel.add( 869 Utilities.createDefaultLabel( 870 LocalizableMessage.raw(OBFUSCATED_VALUE)), gbc); 871 } 872 else if (!isBinary) 873 { 874 Set<String> sValues = toStrings(values); 875 LocalizableMessage text = LocalizableMessage.raw(Utilities.getStringFromCollection(sValues, "\n")); 876 final JTextArea ta; 877 JComponent toAdd; 878 if (values.size() > 15) 879 { 880 ta = Utilities.createNonEditableTextArea(text, 15, 20); 881 toAdd = Utilities.createScrollPane(ta); 882 } 883 else 884 { 885 ta = Utilities.createNonEditableTextArea(text, values.size(), 20); 886 toAdd = ta; 887 } 888 panel.add(toAdd, gbc); 889 break; 890 } 891 else 892 { 893 final BinaryCellPanel pane = new BinaryCellPanel(); 894 pane.setEditButtonText(INFO_CTRL_PANEL_VIEW_BUTTON_LABEL.get()); 895 final byte[] binaryValue = (byte[])o; 896 Schema schema = getInfo().getServerDescriptor().getSchema(); 897 final boolean isImage = Utilities.hasImageSyntax(attrName, schema); 898 pane.setValue(binaryValue, isImage); 899 pane.addEditActionListener(new ActionListener() 900 { 901 /** {@inheritDoc} */ 902 public void actionPerformed(ActionEvent ev) 903 { 904 if (binaryDlg == null) 905 { 906 binaryPanel = new BinaryValuePanel(); 907 binaryPanel.setInfo(getInfo()); 908 binaryDlg = new GenericDialog( 909 Utilities.getFrame(SimplifiedViewEntryPanel.this), 910 binaryPanel); 911 binaryDlg.setModal(true); 912 Utilities.centerGoldenMean(binaryDlg, 913 Utilities.getParentDialog(SimplifiedViewEntryPanel.this)); 914 } 915 binaryPanel.setValue(attrName, binaryValue); 916 binaryDlg.setVisible(true); 917 } 918 }); 919 panel.add(pane, gbc); 920 } 921 } 922 return panel; 923 } 924 925 private Set<String> toStrings(Collection<Object> objects) 926 { 927 Set<String> results = new TreeSet<>(); 928 for (Object o : objects) 929 { 930 results.add(String.valueOf(o)); 931 } 932 return results; 933 } 934 935 private JComponent getReadWriteComponent(final String attrName, 936 List<Object> values) 937 { 938 JPanel panel = new JPanel(new GridBagLayout()); 939 panel.setOpaque(false); 940 GridBagConstraints gbc = new GridBagConstraints(); 941 gbc.gridy = 0; 942 943 List<EditorComponent> components = new ArrayList<>(); 944 hmEditors.put(attrName.toLowerCase(), components); 945 946 boolean isBinary = hasBinaryValue(values); 947 for (Object o : values) 948 { 949 gbc.fill = GridBagConstraints.HORIZONTAL; 950 gbc.weightx = 1.0; 951 gbc.gridx = 0; 952 if (attrName.equalsIgnoreCase( 953 ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME)) 954 { 955 final ObjectClassCellPanel ocCellPanel = new ObjectClassCellPanel(); 956 Schema schema = getInfo().getServerDescriptor().getSchema(); 957 final ObjectClassValue ocDescriptor; 958 if (schema != null) 959 { 960 ocDescriptor = getObjectClassDescriptor(values, schema); 961 ocCellPanel.setValue(ocDescriptor); 962 } 963 else 964 { 965 ocDescriptor = null; 966 } 967 ocCellPanel.addEditActionListener(new ActionListener() 968 { 969 private ObjectClassValue newValue; 970 /** {@inheritDoc} */ 971 public void actionPerformed(ActionEvent ev) 972 { 973 if (editOcDlg == null) 974 { 975 editOcPanel = new ObjectClassEditorPanel(); 976 editOcPanel.setInfo(getInfo()); 977 editOcDlg = new GenericDialog( 978 null, 979 editOcPanel); 980 editOcDlg.setModal(true); 981 Utilities.centerGoldenMean(editOcDlg, 982 Utilities.getParentDialog(SimplifiedViewEntryPanel.this)); 983 } 984 if (newValue == null && ocDescriptor != null) 985 { 986 editOcPanel.setValue(ocDescriptor); 987 } 988 else 989 { 990 editOcPanel.setValue(newValue); 991 } 992 editOcDlg.setVisible(true); 993 if (editOcPanel.valueChanged()) 994 { 995 newValue = editOcPanel.getObjectClassValue(); 996 ocCellPanel.setValue(newValue); 997 updatePanel(newValue); 998 } 999 } 1000 }); 1001 panel = ocCellPanel; 1002 components.add(new EditorComponent(ocCellPanel)); 1003 break; 1004 } 1005 else if (isPassword(attrName) || isConfirmPassword(attrName)) 1006 { 1007 JPasswordField pf = Utilities.createPasswordField(); 1008 if (!o.equals("")) 1009 { 1010 pf.setText(getPasswordStringValue(o)); 1011 } 1012 panel.add(pf, gbc); 1013 components.add(new EditorComponent(pf)); 1014 } 1015 else if (!isBinary) 1016 { 1017 if (isSingleValue(attrName)) 1018 { 1019 final JTextField tf = Utilities.createMediumTextField(); 1020 tf.setText(String.valueOf(o)); 1021 gbc.gridx = 0; 1022 panel.add(tf, gbc); 1023 if (mustAddBrowseButton(attrName)) 1024 { 1025 gbc.insets.left = 5; 1026 gbc.weightx = 0.0; 1027 gbc.gridx ++; 1028 gbc.anchor = GridBagConstraints.NORTH; 1029 JButton browse = Utilities.createButton(INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get()); 1030 browse.addActionListener(new AddBrowseClickedActionListener(tf, attrName)); 1031 panel.add(browse, gbc); 1032 new DropTarget(tf, dropTargetListener); 1033 } 1034 components.add(new EditorComponent(tf)); 1035 } 1036 else 1037 { 1038 Set<String> sValues = toStrings(values); 1039 final LocalizableMessage text = LocalizableMessage.raw(Utilities.getStringFromCollection(sValues, "\n")); 1040 final JTextArea ta; 1041 JComponent toAdd; 1042 if (values.size() > 15) 1043 { 1044 ta = Utilities.createTextArea(text, 15, 20); 1045 toAdd = Utilities.createScrollPane(ta); 1046 } 1047 else 1048 { 1049 ta = Utilities.createTextAreaWithBorder(text, values.size(), 20); 1050 toAdd = ta; 1051 } 1052 panel.add(toAdd, gbc); 1053 if (mustAddBrowseButton(attrName)) 1054 { 1055 gbc.insets.left = 5; 1056 gbc.weightx = 0.0; 1057 gbc.gridx ++; 1058 gbc.anchor = GridBagConstraints.NORTH; 1059 final JButton browse = Utilities.createButton( 1060 INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get()); 1061 browse.addActionListener(new AddBrowseClickedActionListener(ta, attrName)); 1062 if (attrName.equalsIgnoreCase(ServerConstants.ATTR_UNIQUE_MEMBER_LC)) 1063 { 1064 browse.setText( 1065 INFO_CTRL_PANEL_ADD_MEMBERS_BUTTON.get().toString()); 1066 } 1067 panel.add(browse, gbc); 1068 new DropTarget(ta, dropTargetListener); 1069 } 1070 components.add(new EditorComponent(ta)); 1071 } 1072 break; 1073 } 1074 else 1075 { 1076 final BinaryCellPanel pane = new BinaryCellPanel(); 1077 Schema schema = getInfo().getServerDescriptor().getSchema(); 1078 final boolean isImage = Utilities.hasImageSyntax(attrName, schema); 1079 pane.setDisplayDelete(true); 1080 final byte[] binaryValue = (byte[])o; 1081 if (binaryValue.length > 0) 1082 { 1083 pane.setValue(binaryValue, isImage); 1084 } 1085 pane.addEditActionListener(new ActionListener() 1086 { 1087 private BinaryValue newValue; 1088 /** {@inheritDoc} */ 1089 public void actionPerformed(ActionEvent ev) 1090 { 1091 if (editBinaryDlg == null) 1092 { 1093 editBinaryPanel = new BinaryAttributeEditorPanel(); 1094 editBinaryPanel.setInfo(getInfo()); 1095 editBinaryDlg = new GenericDialog( 1096 Utilities.getFrame(SimplifiedViewEntryPanel.this), 1097 editBinaryPanel); 1098 editBinaryDlg.setModal(true); 1099 Utilities.centerGoldenMean(editBinaryDlg, 1100 Utilities.getParentDialog(SimplifiedViewEntryPanel.this)); 1101 } 1102 if (newValue == null) 1103 { 1104 // We use an empty binary array to not breaking the logic: 1105 // it means that there is no value for the attribute. 1106 if (binaryValue != null && binaryValue.length > 0) 1107 { 1108 newValue = BinaryValue.createBase64(binaryValue); 1109 editBinaryPanel.setValue(attrName, newValue); 1110 } 1111 else 1112 { 1113 editBinaryPanel.setValue(attrName, null); 1114 } 1115 } 1116 else 1117 { 1118 editBinaryPanel.setValue(attrName, newValue); 1119 } 1120 editBinaryDlg.setVisible(true); 1121 if (editBinaryPanel.valueChanged()) 1122 { 1123 newValue = editBinaryPanel.getBinaryValue(); 1124 pane.setValue(newValue, isImage); 1125 notifyListeners(); 1126 } 1127 } 1128 }); 1129 pane.addDeleteActionListener(new ActionListener() 1130 { 1131 /** {@inheritDoc} */ 1132 public void actionPerformed(ActionEvent ev) 1133 { 1134 pane.setValue((byte[])null, false); 1135 if (editBinaryPanel != null) 1136 { 1137 editBinaryPanel.setValue(attrName, null); 1138 } 1139 notifyListeners(); 1140 } 1141 }); 1142 panel.add(pane, gbc); 1143 components.add(new EditorComponent(pane)); 1144 } 1145 gbc.gridy ++; 1146 gbc.insets.top = 10; 1147 } 1148 return panel; 1149 } 1150 1151 private boolean isSingleValue(String attrName) 1152 { 1153 Schema schema = getInfo().getServerDescriptor().getSchema(); 1154 if (schema != null && schema.hasAttributeType(attrName)) 1155 { 1156 AttributeType attr = schema.getAttributeType(attrName); 1157 return attr.isSingleValue(); 1158 } 1159 return false; 1160 } 1161 1162 private boolean isRequired(String attrName, CustomSearchResult sr) 1163 { 1164 attrName = Utilities.getAttributeNameWithoutOptions(attrName); 1165 1166 Schema schema = getInfo().getServerDescriptor().getSchema(); 1167 if (schema != null && schema.hasAttributeType(attrName)) 1168 { 1169 AttributeType attr = schema.getAttributeType(attrName); 1170 List<Object> ocs = sr.getAttributeValues(ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME); 1171 for (Object o : ocs) 1172 { 1173 String oc = (String) o; 1174 ObjectClass objectClass = schema.getObjectClass(oc.toLowerCase()); 1175 if (objectClass != null && objectClass.isRequired(attr)) 1176 { 1177 return true; 1178 } 1179 } 1180 } 1181 return false; 1182 } 1183 1184 /** {@inheritDoc} */ 1185 public GenericDialog.ButtonType getButtonType() 1186 { 1187 return GenericDialog.ButtonType.NO_BUTTON; 1188 } 1189 1190 /** {@inheritDoc} */ 1191 public Entry getEntry() throws OpenDsException 1192 { 1193 Entry entry = null; 1194 1195 ArrayList<LocalizableMessage> errors = new ArrayList<>(); 1196 1197 try 1198 { 1199 DN.valueOf(getDisplayedDN()); 1200 } 1201 catch (Throwable t) 1202 { 1203 errors.add(ERR_CTRL_PANEL_DN_NOT_VALID.get()); 1204 } 1205 1206 for (String attrName : hmLabels.keySet()) 1207 { 1208 setPrimaryValid(hmLabels.get(attrName)); 1209 } 1210 1211 1212 // Check passwords 1213 for (String attrName : lastUserPasswords.keySet()) 1214 { 1215 List<String> pwds = getNewPasswords(attrName); 1216 List<String> confirmPwds = getConfirmPasswords(attrName); 1217 if (!pwds.equals(confirmPwds)) 1218 { 1219 setPrimaryInvalid(hmLabels.get(attrName)); 1220 setPrimaryInvalid(hmLabels.get(getConfirmPasswordKey(attrName))); 1221 LocalizableMessage msg = ERR_CTRL_PANEL_PASSWORD_DO_NOT_MATCH.get(); 1222 if (!errors.contains(msg)) 1223 { 1224 errors.add(msg); 1225 } 1226 } 1227 } 1228 for (String attrName : requiredAttrs) 1229 { 1230 if (!hasValue(attrName)) 1231 { 1232 setPrimaryInvalid(hmLabels.get(attrName)); 1233 errors.add(ERR_CTRL_PANEL_ATTRIBUTE_REQUIRED.get( 1234 hmDisplayedNames.get(attrName))); 1235 } 1236 } 1237 1238 if (!errors.isEmpty()) 1239 { 1240 throw new CheckEntrySyntaxException(errors); 1241 } 1242 1243 LDIFImportConfig ldifImportConfig = null; 1244 try 1245 { 1246 String ldif = getLDIF(); 1247 1248 ldifImportConfig = new LDIFImportConfig(new StringReader(ldif)); 1249 LDIFReader reader = new LDIFReader(ldifImportConfig); 1250 entry = reader.readEntry(checkSchema()); 1251 addValuesInRDN(entry); 1252 } 1253 catch (IOException ioe) 1254 { 1255 throw new OnlineUpdateException( 1256 ERR_CTRL_PANEL_ERROR_CHECKING_ENTRY.get(ioe), ioe); 1257 } 1258 finally 1259 { 1260 if (ldifImportConfig != null) 1261 { 1262 ldifImportConfig.close(); 1263 } 1264 } 1265 return entry; 1266 } 1267 1268 private List<String> getDisplayedStringValues(String attrName) 1269 { 1270 List<String> values = new ArrayList<>(); 1271 List<EditorComponent> comps = 1272 hmEditors.get(attrName.toLowerCase()); 1273 if (comps != null) 1274 { 1275 for (EditorComponent comp : comps) 1276 { 1277 Object value = comp.getValue(); 1278 if (value instanceof ObjectClassValue) 1279 { 1280 ObjectClassValue ocValue = (ObjectClassValue)value; 1281 if (ocValue.getStructural() != null) 1282 { 1283 values.add(ocValue.getStructural()); 1284 } 1285 values.addAll(ocValue.getAuxiliary()); 1286 } 1287 else if (value instanceof Collection<?>) 1288 { 1289 for (Object o : (Collection<?>)value) 1290 { 1291 values.add((String)o); 1292 } 1293 } 1294 else 1295 { 1296 values.add(String.valueOf(comp.getValue())); 1297 } 1298 } 1299 } 1300 return values; 1301 } 1302 1303 private List<String> getNewPasswords(String attrName) 1304 { 1305 String attr = 1306 Utilities.getAttributeNameWithoutOptions(attrName).toLowerCase(); 1307 return getDisplayedStringValues(attr); 1308 } 1309 1310 private List<String> getConfirmPasswords(String attrName) 1311 { 1312 return getDisplayedStringValues(getConfirmPasswordKey(attrName)); 1313 } 1314 1315 private String getConfirmPasswordKey(String attrName) 1316 { 1317 return CONFIRM_PASSWORD+ 1318 Utilities.getAttributeNameWithoutOptions(attrName).toLowerCase(); 1319 } 1320 1321 private boolean isConfirmPassword(String key) 1322 { 1323 return key.startsWith(CONFIRM_PASSWORD); 1324 } 1325 1326 /** 1327 * Returns the LDIF representation of the displayed entry. 1328 * @return the LDIF representation of the displayed entry. 1329 */ 1330 private String getLDIF() 1331 { 1332 StringBuilder sb = new StringBuilder(); 1333 sb.append("dn: ").append(getDisplayedDN()); 1334 1335 for (String attrName : hmEditors.keySet()) 1336 { 1337 if (isConfirmPassword(attrName)) 1338 { 1339 continue; 1340 } 1341 else if (isPassword(attrName)) 1342 { 1343 List<String> newPwds = getNewPasswords(attrName); 1344 if (newPwds.equals(lastUserPasswords.get(attrName.toLowerCase()))) 1345 { 1346 List<Object> oldValues = searchResult.getAttributeValues(attrName); 1347 if (!oldValues.isEmpty()) 1348 { 1349 appendLDIFLines(sb, attrName, oldValues); 1350 } 1351 } 1352 else 1353 { 1354 appendLDIFLines(sb, attrName); 1355 } 1356 } 1357 else 1358 if (!schemaReadOnlyAttributesLowerCase.contains(attrName.toLowerCase())) 1359 { 1360 appendLDIFLines(sb, attrName); 1361 } 1362 } 1363 1364 // Add the attributes that are not displayed 1365 for (String attrName : schemaReadOnlyAttributesLowerCase) 1366 { 1367 List<Object> values = searchResult.getAttributeValues(attrName); 1368 if (!values.isEmpty()) 1369 { 1370 appendLDIFLines(sb, attrName, values); 1371 } 1372 } 1373 return sb.toString(); 1374 } 1375 1376 private boolean isAttrName(String attrName, CustomSearchResult sr) 1377 { 1378 List<Object> values = sr.getAttributeValues(ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME); 1379 for (Object o : values) 1380 { 1381 String ocName = (String)o; 1382 String attr = hmNameAttrNames.get(ocName.toLowerCase()); 1383 if (attr != null && attr.equalsIgnoreCase(attrName)) 1384 { 1385 return true; 1386 } 1387 } 1388 return false; 1389 } 1390 1391 private boolean hasBinaryValue(List<Object> values) 1392 { 1393 return !values.isEmpty() && values.iterator().next() instanceof byte[]; 1394 } 1395 1396 private boolean mustAddBrowseButton(String attrName) 1397 { 1398 if (attrName.equalsIgnoreCase(ServerConstants.ATTR_UNIQUE_MEMBER_LC) 1399 || attrName.equalsIgnoreCase("ds-target-group-dn")) 1400 { 1401 return true; 1402 } 1403 Schema schema = getInfo().getServerDescriptor().getSchema(); 1404 if (schema != null && schema.hasAttributeType(attrName)) 1405 { 1406 AttributeType attr = schema.getAttributeType(attrName); 1407 // There is no name for a regex syntax. 1408 String syntaxName = attr.getSyntax().getName(); 1409 if (syntaxName != null) 1410 { 1411 return syntaxName.equalsIgnoreCase(SchemaConstants.SYNTAX_DN_NAME); 1412 } 1413 } 1414 return false; 1415 } 1416 1417 /** {@inheritDoc} */ 1418 @Override 1419 protected List<Object> getValues(String attrName) 1420 { 1421 List<Object> values = new ArrayList<>(); 1422 for (EditorComponent comp : hmEditors.get(attrName)) 1423 { 1424 if (hasValue(comp)) 1425 { 1426 Object value = comp.getValue(); 1427 if (value instanceof Collection<?>) 1428 { 1429 values.addAll((Collection<?>) value); 1430 } 1431 else 1432 { 1433 values.add(value); 1434 } 1435 } 1436 } 1437 return values; 1438 } 1439 1440 private void appendLDIFLines(StringBuilder sb, String attrName) 1441 { 1442 List<Object> values = getValues(attrName); 1443 if (!values.isEmpty()) 1444 { 1445 appendLDIFLines(sb, attrName, values); 1446 } 1447 } 1448 1449 /** {@inheritDoc} */ 1450 protected String getDisplayedDN() 1451 { 1452 StringBuilder sb = new StringBuilder(); 1453 try 1454 { 1455 DN oldDN = DN.valueOf(searchResult.getDN()); 1456 if (oldDN.size() > 0) 1457 { 1458 RDN rdn = oldDN.rdn(); 1459 List<AVA> avas = new ArrayList<>(); 1460 for (AVA ava : rdn) 1461 { 1462 AttributeType attrType = ava.getAttributeType(); 1463 String attrName = ava.getAttributeName(); 1464 ByteString value = ava.getAttributeValue(); 1465 1466 List<String> values = getDisplayedStringValues(attrName); 1467 if (!values.contains(value.toString())) 1468 { 1469 if (!values.isEmpty()) 1470 { 1471 String firstNonEmpty = getFirstNonEmpty(values); 1472 if (firstNonEmpty != null) 1473 { 1474 avas.add(new AVA(attrType, attrName, ByteString.valueOfUtf8(firstNonEmpty))); 1475 } 1476 } 1477 } 1478 else 1479 { 1480 avas.add(new AVA(attrType, attrName, value)); 1481 } 1482 } 1483 if (avas.isEmpty()) 1484 { 1485 // Check the attributes in the order that we display them and use 1486 // the first one. 1487 Schema schema = getInfo().getServerDescriptor().getSchema(); 1488 if (schema != null) 1489 { 1490 for (String attrName : hmEditors.keySet()) 1491 { 1492 if (isPassword(attrName) || 1493 isConfirmPassword(attrName)) 1494 { 1495 continue; 1496 } 1497 List<EditorComponent> comps = hmEditors.get(attrName); 1498 if (!comps.isEmpty()) 1499 { 1500 Object o = comps.iterator().next().getValue(); 1501 if (o instanceof String) 1502 { 1503 String aName = Utilities.getAttributeNameWithoutOptions(attrName); 1504 if (schema.hasAttributeType(aName)) 1505 { 1506 avas.add(new AVA(schema.getAttributeType(aName), aName, o)); 1507 } 1508 break; 1509 } 1510 } 1511 } 1512 } 1513 } 1514 DN parent = oldDN.parent(); 1515 if (!avas.isEmpty()) 1516 { 1517 RDN newRDN = new RDN(avas); 1518 1519 DN newDN; 1520 if (parent == null) 1521 { 1522 newDN = DN.rootDN().child(newRDN); 1523 } 1524 else 1525 { 1526 newDN = parent.child(newRDN); 1527 } 1528 sb.append(newDN); 1529 } 1530 else 1531 { 1532 if (parent != null) 1533 { 1534 sb.append(",").append(parent); 1535 } 1536 } 1537 } 1538 } 1539 catch (Throwable t) 1540 { 1541 throw new RuntimeException("Unexpected error: "+t, t); 1542 } 1543 return sb.toString(); 1544 } 1545 1546 private String getFirstNonEmpty(List<String> values) 1547 { 1548 for (String v : values) 1549 { 1550 v = v.trim(); 1551 if (v.length() > 0) 1552 { 1553 return v; 1554 } 1555 } 1556 return null; 1557 } 1558 1559 private void addBrowseClicked(String attrName, JTextComponent textComponent) 1560 { 1561 LocalizableMessage previousTitle = null; 1562 LDAPEntrySelectionPanel.Filter previousFilter = null; 1563 LocalizableMessage title; 1564 LDAPEntrySelectionPanel.Filter filter; 1565 if (browseEntriesDlg == null) 1566 { 1567 browseEntriesPanel = new LDAPEntrySelectionPanel(); 1568 browseEntriesPanel.setMultipleSelection(false); 1569 browseEntriesPanel.setInfo(getInfo()); 1570 browseEntriesDlg = new GenericDialog(Utilities.getFrame(this), 1571 browseEntriesPanel); 1572 Utilities.centerGoldenMean(browseEntriesDlg, 1573 Utilities.getParentDialog(this)); 1574 browseEntriesDlg.setModal(true); 1575 } 1576 else 1577 { 1578 previousTitle = browseEntriesPanel.getTitle(); 1579 previousFilter = browseEntriesPanel.getFilter(); 1580 } 1581 if (attrName.equalsIgnoreCase(ServerConstants.ATTR_UNIQUE_MEMBER_LC)) 1582 { 1583 title = INFO_CTRL_PANEL_ADD_MEMBERS_LABEL.get(); 1584 filter = LDAPEntrySelectionPanel.Filter.USERS; 1585 } 1586 else if (attrName.equalsIgnoreCase("ds-target-group-dn")) 1587 { 1588 title = INFO_CTRL_PANEL_CHOOSE_REFERENCE_GROUP.get(); 1589 filter = LDAPEntrySelectionPanel.Filter.DYNAMIC_GROUPS; 1590 } 1591 else 1592 { 1593 title = INFO_CTRL_PANEL_CHOOSE_ENTRIES.get(); 1594 filter = LDAPEntrySelectionPanel.Filter.DEFAULT; 1595 } 1596 if (!title.equals(previousTitle)) 1597 { 1598 browseEntriesPanel.setTitle(title); 1599 } 1600 if (!filter.equals(previousFilter)) 1601 { 1602 browseEntriesPanel.setFilter(filter); 1603 } 1604 browseEntriesPanel.setMultipleSelection(!isSingleValue(attrName)); 1605 1606 browseEntriesDlg.setVisible(true); 1607 String[] dns = browseEntriesPanel.getDNs(); 1608 if (dns.length > 0) 1609 { 1610 if (textComponent instanceof JTextArea) 1611 { 1612 StringBuilder sb = new StringBuilder(); 1613 sb.append(textComponent.getText()); 1614 for (String dn : dns) 1615 { 1616 if (sb.length() > 0) 1617 { 1618 sb.append("\n"); 1619 } 1620 sb.append(dn); 1621 } 1622 textComponent.setText(sb.toString()); 1623 textComponent.setCaretPosition(sb.length()); 1624 } 1625 else 1626 { 1627 textComponent.setText(dns[0]); 1628 } 1629 } 1630 } 1631 1632 private String getPasswordStringValue(Object o) 1633 { 1634 if (o instanceof byte[]) 1635 { 1636 return Base64.encode((byte[])o); 1637 } 1638 else 1639 { 1640 return String.valueOf(o); 1641 } 1642 } 1643 1644 private void updatePanel(ObjectClassValue newValue) 1645 { 1646 CustomSearchResult oldResult = searchResult; 1647 CustomSearchResult newResult = new CustomSearchResult(searchResult.getDN()); 1648 1649 for (String attrName : schemaReadOnlyAttributesLowerCase) 1650 { 1651 List<Object> values = searchResult.getAttributeValues(attrName); 1652 if (!values.isEmpty()) 1653 { 1654 newResult.set(attrName, values); 1655 } 1656 } 1657 ignoreEntryChangeEvents = true; 1658 1659 Schema schema = getInfo().getServerDescriptor().getSchema(); 1660 if (schema != null) 1661 { 1662 ArrayList<String> attributes = new ArrayList<>(); 1663 ArrayList<String> ocs = new ArrayList<>(); 1664 if (newValue.getStructural() != null) 1665 { 1666 ocs.add(newValue.getStructural().toLowerCase()); 1667 } 1668 for (String oc : newValue.getAuxiliary()) 1669 { 1670 ocs.add(oc.toLowerCase()); 1671 } 1672 for (String oc : ocs) 1673 { 1674 ObjectClass objectClass = schema.getObjectClass(oc); 1675 if (objectClass != null) 1676 { 1677 for (AttributeType attr : objectClass.getRequiredAttributeChain()) 1678 { 1679 attributes.add(attr.getNameOrOID().toLowerCase()); 1680 } 1681 for (AttributeType attr : objectClass.getOptionalAttributeChain()) 1682 { 1683 attributes.add(attr.getNameOrOID().toLowerCase()); 1684 } 1685 } 1686 } 1687 for (String attrName : editableOperationalAttrNames) 1688 { 1689 attributes.add(attrName.toLowerCase()); 1690 } 1691 for (String attrName : hmEditors.keySet()) 1692 { 1693 String attrNoOptions = 1694 Utilities.getAttributeNameWithoutOptions(attrName); 1695 if (!attributes.contains(attrNoOptions)) 1696 { 1697 continue; 1698 } 1699 if (isPassword(attrName)) 1700 { 1701 List<String> newPwds = getNewPasswords(attrName); 1702 if (newPwds.equals(lastUserPasswords.get(attrName))) 1703 { 1704 List<Object> oldValues = searchResult.getAttributeValues(attrName); 1705 newResult.set(attrName, oldValues); 1706 } 1707 else 1708 { 1709 setValues(newResult, attrName); 1710 } 1711 } 1712 else if (!schemaReadOnlyAttributesLowerCase.contains( 1713 attrName.toLowerCase())) 1714 { 1715 setValues(newResult, attrName); 1716 } 1717 } 1718 } 1719 update(newResult, isReadOnly, treePath); 1720 ignoreEntryChangeEvents = false; 1721 searchResult = oldResult; 1722 notifyListeners(); 1723 } 1724 1725 private void updateAttributeVisibility(boolean showAll) 1726 { 1727 for (String attrName : hmLabels.keySet()) 1728 { 1729 Component label = hmLabels.get(attrName); 1730 Component comp = hmComponents.get(attrName); 1731 1732 if (showAll || requiredAttrs.contains(attrName)) 1733 { 1734 label.setVisible(true); 1735 comp.setVisible(true); 1736 } 1737 else 1738 { 1739 List<EditorComponent> editors = hmEditors.get(attrName); 1740 boolean hasValue = false; 1741 1742 for (EditorComponent editor : editors) 1743 { 1744 hasValue = hasValue(editor); 1745 if (hasValue) 1746 { 1747 break; 1748 } 1749 } 1750 label.setVisible(hasValue); 1751 comp.setVisible(hasValue); 1752 } 1753 } 1754 repaint(); 1755 } 1756 1757 private boolean hasValue(String attrName) 1758 { 1759 return !getValues(attrName).isEmpty(); 1760 } 1761 1762 private boolean hasValue(EditorComponent editor) 1763 { 1764 Object value = editor.getValue(); 1765 if (value instanceof byte[]) 1766 { 1767 return ((byte[])value).length > 0; 1768 } 1769 else if (value instanceof String) 1770 { 1771 return ((String)value).trim().length() > 0; 1772 } 1773 else if (value instanceof Collection<?>) 1774 { 1775 return !((Collection<?>)value).isEmpty(); 1776 } 1777 return value != null; 1778 } 1779 1780 /** Calls #addBrowseClicked(). */ 1781 private final class AddBrowseClickedActionListener implements ActionListener 1782 { 1783 private final JTextComponent tc; 1784 private final String attrName; 1785 1786 private AddBrowseClickedActionListener(JTextComponent tc, String attrName) 1787 { 1788 this.tc = tc; 1789 this.attrName = attrName; 1790 } 1791 1792 public void actionPerformed(ActionEvent ev) 1793 { 1794 addBrowseClicked(attrName, tc); 1795 } 1796 } 1797 1798 /** 1799 * A class that makes an association between a component (JTextField, a 1800 * BinaryCellValue...) and the associated value that will be used to create 1801 * the modified entry corresponding to the contents of the panel. 1802 * 1803 */ 1804 class EditorComponent 1805 { 1806 private Component comp; 1807 1808 /** 1809 * Creates an EditorComponent using a text component. 1810 * @param tf the text component. 1811 */ 1812 public EditorComponent(JTextComponent tf) 1813 { 1814 comp = tf; 1815 tf.getDocument().addDocumentListener(new DocumentListener() 1816 { 1817 /** {@inheritDoc} */ 1818 public void insertUpdate(DocumentEvent ev) 1819 { 1820 notifyListeners(); 1821 } 1822 1823 /** {@inheritDoc} */ 1824 public void changedUpdate(DocumentEvent ev) 1825 { 1826 notifyListeners(); 1827 } 1828 1829 /** {@inheritDoc} */ 1830 public void removeUpdate(DocumentEvent ev) 1831 { 1832 notifyListeners(); 1833 } 1834 }); 1835 } 1836 1837 /** 1838 * Creates an EditorComponent using a BinaryCellPanel. 1839 * @param binaryPanel the BinaryCellPanel. 1840 */ 1841 public EditorComponent(BinaryCellPanel binaryPanel) 1842 { 1843 comp = binaryPanel; 1844 } 1845 1846 /** 1847 * Creates an EditorComponent using a ObjectClassCellPanel. 1848 * @param ocPanel the ObjectClassCellPanel. 1849 */ 1850 public EditorComponent(ObjectClassCellPanel ocPanel) 1851 { 1852 comp = ocPanel; 1853 } 1854 1855 /** 1856 * Returns the value that the component is displaying. The returned value 1857 * is a Set of Strings (for multi-valued attributes), a byte[] for binary 1858 * values or a String for single-valued attributes. Single-valued 1859 * attributes refer to the definition in the schema (and not to the fact 1860 * that there is a single value for the attribute in this entry). 1861 * @return the value that the component is displaying. 1862 */ 1863 public Object getValue() 1864 { 1865 Object returnValue; 1866 if (comp instanceof ObjectClassCellPanel) 1867 { 1868 ObjectClassValue ocDesc = ((ObjectClassCellPanel)comp).getValue(); 1869 LinkedHashSet<String> values = new LinkedHashSet<>(); 1870 String structural = ocDesc.getStructural(); 1871 if (structural != null) 1872 { 1873 values.add(structural); 1874 } 1875 values.addAll(ocDesc.getAuxiliary()); 1876 Schema schema = getInfo().getServerDescriptor().getSchema(); 1877 if (schema != null && structural != null) 1878 { 1879 ObjectClass oc = schema.getObjectClass(structural.toLowerCase()); 1880 if (oc != null) 1881 { 1882 values.addAll(getObjectClassSuperiorValues(oc)); 1883 } 1884 } 1885 returnValue = values; 1886 } else if (comp instanceof JTextArea) 1887 { 1888 LinkedHashSet<String> values = new LinkedHashSet<>(); 1889 String value = ((JTextArea)comp).getText(); 1890 String[] lines = value.split("\n"); 1891 for (String line : lines) 1892 { 1893 line = line.trim(); 1894 if (line.length() > 0) 1895 { 1896 values.add(line); 1897 } 1898 } 1899 returnValue = values; 1900 } 1901 else if (comp instanceof JTextComponent) 1902 { 1903 returnValue = ((JTextComponent)comp).getText(); 1904 } 1905 else 1906 { 1907 Object o = ((BinaryCellPanel)comp).getValue(); 1908 if (o instanceof BinaryValue) 1909 { 1910 try 1911 { 1912 returnValue = ((BinaryValue)o).getBytes(); 1913 } 1914 catch (ParseException pe) 1915 { 1916 throw new RuntimeException("Unexpected error: "+pe); 1917 } 1918 } 1919 else 1920 { 1921 returnValue = o; 1922 } 1923 } 1924 return returnValue; 1925 } 1926 } 1927} 1928