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