001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.guitools.controlpanel.ui; 028 029import static org.opends.messages.AdminToolMessages.*; 030 031import java.awt.Component; 032import java.awt.GridBagConstraints; 033import java.awt.Insets; 034import java.awt.Point; 035import java.awt.event.ActionEvent; 036import java.awt.event.ActionListener; 037import java.io.IOException; 038import java.io.StringReader; 039import java.util.ArrayList; 040import java.util.Collection; 041import java.util.Comparator; 042import java.util.HashSet; 043import java.util.LinkedHashSet; 044import java.util.List; 045import java.util.Set; 046import java.util.SortedSet; 047import java.util.TreeSet; 048 049import javax.swing.Box; 050import javax.swing.JCheckBox; 051import javax.swing.JLabel; 052import javax.swing.JScrollPane; 053import javax.swing.JTable; 054import javax.swing.SwingUtilities; 055import javax.swing.tree.TreePath; 056 057import org.opends.guitools.controlpanel.datamodel.BinaryValue; 058import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; 059import org.opends.guitools.controlpanel.datamodel.ObjectClassValue; 060import org.opends.guitools.controlpanel.datamodel.SortableTableModel; 061import org.opends.guitools.controlpanel.task.OnlineUpdateException; 062import org.opends.guitools.controlpanel.ui.renderer.AttributeCellEditor; 063import org.opends.guitools.controlpanel.ui.renderer.LDAPEntryTableCellRenderer; 064import org.opends.guitools.controlpanel.util.Utilities; 065import org.forgerock.i18n.LocalizableMessage; 066import org.forgerock.opendj.ldap.ByteString; 067import org.opends.server.types.*; 068import org.opends.server.util.LDIFReader; 069import org.opends.server.util.ServerConstants; 070 071/** 072 * The panel displaying a table view of an LDAP entry. 073 */ 074public class TableViewEntryPanel extends ViewEntryPanel 075{ 076 private static final long serialVersionUID = 2135331526526472175L; 077 private CustomSearchResult searchResult; 078 private LDAPEntryTableModel tableModel; 079 private LDAPEntryTableCellRenderer renderer; 080 private JTable table; 081 private boolean isReadOnly; 082 private TreePath treePath; 083 private JScrollPane scroll; 084 private AttributeCellEditor editor; 085 private JLabel requiredLabel; 086 private JCheckBox showOnlyAttrsWithValues; 087 088 /** 089 * Default constructor. 090 * 091 */ 092 public TableViewEntryPanel() 093 { 094 super(); 095 createLayout(); 096 } 097 098 /** {@inheritDoc} */ 099 public Component getPreferredFocusComponent() 100 { 101 return table; 102 } 103 104 /** 105 * Creates the layout of the panel (but the contents are not populated here). 106 */ 107 private void createLayout() 108 { 109 GridBagConstraints gbc = new GridBagConstraints(); 110 gbc.gridx = 0; 111 gbc.gridy = 0; 112 gbc.gridwidth = 2; 113 gbc.fill = GridBagConstraints.NONE; 114 gbc.anchor = GridBagConstraints.WEST; 115 gbc.weightx = 1.0; 116 117 addTitlePanel(this, gbc); 118 119 gbc.gridy ++; 120 gbc.insets.top = 5; 121 gbc.gridwidth = 1; 122 showOnlyAttrsWithValues = Utilities.createCheckBox( 123 INFO_CTRL_PANEL_SHOW_ATTRS_WITH_VALUES_LABEL.get()); 124 showOnlyAttrsWithValues.setSelected(displayOnlyWithAttrs); 125 showOnlyAttrsWithValues.addActionListener(new ActionListener() 126 { 127 /** {@inheritDoc} */ 128 public void actionPerformed(ActionEvent ev) 129 { 130 updateAttributeVisibility(); 131 displayOnlyWithAttrs = showOnlyAttrsWithValues.isSelected(); 132 } 133 }); 134 gbc.weightx = 0.0; 135 gbc.anchor = GridBagConstraints.WEST; 136 add(showOnlyAttrsWithValues, gbc); 137 138 gbc.gridx ++; 139 gbc.anchor = GridBagConstraints.EAST; 140 gbc.fill = GridBagConstraints.NONE; 141 requiredLabel = createRequiredLabel(); 142 add(requiredLabel, gbc); 143 gbc.insets = new Insets(0, 0, 0, 0); 144 add(Box.createVerticalStrut(10), gbc); 145 146 showOnlyAttrsWithValues.setFont(requiredLabel.getFont()); 147 148 gbc.gridy ++; 149 gbc.gridx = 0; 150 gbc.insets.top = 10; 151 gbc.gridwidth = 2; 152 tableModel = new LDAPEntryTableModel(); 153 renderer = new LDAPEntryTableCellRenderer(); 154 table = Utilities.createSortableTable(tableModel, renderer); 155 renderer.setTable(table); 156 editor = new AttributeCellEditor(); 157 table.getColumnModel().getColumn(1).setCellEditor(editor); 158 gbc.weighty = 1.0; 159 gbc.fill = GridBagConstraints.BOTH; 160 gbc.gridy ++; 161 scroll = Utilities.createScrollPane(table); 162 add(scroll, gbc); 163 } 164 165 /** {@inheritDoc} */ 166 public void update(CustomSearchResult sr, boolean isReadOnly, TreePath path) 167 { 168 boolean sameEntry = false; 169 if (searchResult != null && sr != null) 170 { 171 sameEntry = searchResult.getDN().equals(sr.getDN()); 172 } 173 174 searchResult = sr; 175 final Point p = sameEntry ? scroll.getViewport().getViewPosition() : 176 new Point(0, 0); 177 renderer.setSchema(getInfo().getServerDescriptor().getSchema()); 178 editor.setInfo(getInfo()); 179 requiredLabel.setVisible(!isReadOnly); 180 this.isReadOnly = isReadOnly; 181 this.treePath = path; 182 updateTitle(sr, path); 183 ignoreEntryChangeEvents = true; 184 tableModel.displayEntry(); 185 Utilities.updateTableSizes(table); 186 Utilities.updateScrollMode(scroll, table); 187 SwingUtilities.invokeLater(new Runnable() 188 { 189 public void run() 190 { 191 if (p != null && scroll.getViewport().contains(p)) 192 { 193 scroll.getViewport().setViewPosition(p); 194 } 195 ignoreEntryChangeEvents = false; 196 } 197 }); 198 } 199 200 /** {@inheritDoc} */ 201 public GenericDialog.ButtonType getButtonType() 202 { 203 return GenericDialog.ButtonType.NO_BUTTON; 204 } 205 206 /** {@inheritDoc} */ 207 public Entry getEntry() throws OpenDsException 208 { 209 if (SwingUtilities.isEventDispatchThread()) 210 { 211 editor.stopCellEditing(); 212 } 213 else 214 { 215 try 216 { 217 SwingUtilities.invokeAndWait(new Runnable() 218 { 219 public void run() 220 { 221 editor.stopCellEditing(); 222 } 223 }); 224 } 225 catch (Throwable t) 226 { 227 } 228 } 229 Entry entry = null; 230 LDIFImportConfig ldifImportConfig = null; 231 try 232 { 233 String ldif = getLDIF(); 234 235 ldifImportConfig = new LDIFImportConfig(new StringReader(ldif)); 236 LDIFReader reader = new LDIFReader(ldifImportConfig); 237 entry = reader.readEntry(checkSchema()); 238 addValuesInRDN(entry); 239 240 } 241 catch (IOException ioe) 242 { 243 throw new OnlineUpdateException( 244 ERR_CTRL_PANEL_ERROR_CHECKING_ENTRY.get(ioe), ioe); 245 } 246 finally 247 { 248 if (ldifImportConfig != null) 249 { 250 ldifImportConfig.close(); 251 } 252 } 253 return entry; 254 } 255 256 /** 257 * Returns the LDIF representation of the displayed entry. 258 * @return the LDIF representation of the displayed entry. 259 */ 260 private String getLDIF() 261 { 262 StringBuilder sb = new StringBuilder(); 263 sb.append("dn: ").append(getDisplayedDN()); 264 for (int i=0; i<tableModel.getRowCount(); i++) 265 { 266 String attrName = (String)tableModel.getValueAt(i, 0); 267 if (!schemaReadOnlyAttributesLowerCase.contains(attrName.toLowerCase())) 268 { 269 Object value = tableModel.getValueAt(i, 1); 270 appendLDIFLine(sb, attrName, value); 271 } 272 } 273 return sb.toString(); 274 } 275 276 /** {@inheritDoc} */ 277 protected String getDisplayedDN() 278 { 279 StringBuilder sb = new StringBuilder(); 280 try 281 { 282 DN oldDN = DN.valueOf(searchResult.getDN()); 283 if (oldDN.size() > 0) 284 { 285 RDN rdn = oldDN.rdn(); 286 List<AttributeType> attributeTypes = new ArrayList<>(); 287 List<String> attributeNames = new ArrayList<>(); 288 List<ByteString> attributeValues = new ArrayList<>(); 289 for (int i=0; i<rdn.getNumValues(); i++) 290 { 291 String attrName = rdn.getAttributeName(i); 292 ByteString value = rdn.getAttributeValue(i); 293 294 Set<String> values = getDisplayedStringValues(attrName); 295 if (!values.contains(value.toString())) 296 { 297 if (!values.isEmpty()) 298 { 299 String firstNonEmpty = getFirstNonEmpty(values); 300 if (firstNonEmpty != null) 301 { 302 AttributeType attr = rdn.getAttributeType(i); 303 attributeTypes.add(attr); 304 attributeNames.add(rdn.getAttributeName(i)); 305 attributeValues.add(ByteString.valueOfUtf8(firstNonEmpty)); 306 } 307 } 308 } 309 else 310 { 311 attributeTypes.add(rdn.getAttributeType(i)); 312 attributeNames.add(rdn.getAttributeName(i)); 313 attributeValues.add(value); 314 } 315 } 316 if (attributeTypes.isEmpty()) 317 { 318 // Check the attributes in the order that we display them and use 319 // the first one. 320 Schema schema = getInfo().getServerDescriptor().getSchema(); 321 if (schema != null) 322 { 323 for (int i=0; i<table.getRowCount(); i++) 324 { 325 String attrName = (String)table.getValueAt(i, 0); 326 if (isPassword(attrName) || 327 attrName.equals( 328 ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME) || 329 !table.isCellEditable(i, 1)) 330 { 331 continue; 332 } 333 Object o = table.getValueAt(i, 1); 334 if (o instanceof String) 335 { 336 String aName = 337 Utilities.getAttributeNameWithoutOptions(attrName); 338 AttributeType attr = 339 schema.getAttributeType(aName.toLowerCase()); 340 if (attr != null) 341 { 342 attributeTypes.add(attr); 343 attributeNames.add(attrName); 344 attributeValues.add(ByteString.valueOfUtf8((String) o)); 345 } 346 break; 347 } 348 } 349 } 350 } 351 DN parent = oldDN.parent(); 352 if (!attributeTypes.isEmpty()) 353 { 354 RDN newRDN = new RDN(attributeTypes, attributeNames, attributeValues); 355 356 DN newDN; 357 if (parent == null) 358 { 359 newDN = new DN(new RDN[]{newRDN}); 360 } 361 else 362 { 363 newDN = parent.child(newRDN); 364 } 365 sb.append(newDN); 366 } 367 else 368 { 369 if (parent != null) 370 { 371 sb.append(",").append(parent); 372 } 373 } 374 } 375 } 376 catch (Throwable t) 377 { 378 throw new RuntimeException("Unexpected error: "+t, t); 379 } 380 return sb.toString(); 381 } 382 383 private String getFirstNonEmpty(Set<String> values) 384 { 385 for (String v : values) 386 { 387 v = v.trim(); 388 if (v.length() > 0) 389 { 390 return v; 391 } 392 } 393 return null; 394 } 395 396 private Set<String> getDisplayedStringValues(String attrName) 397 { 398 Set<String> values = new LinkedHashSet<>(); 399 for (int i=0; i<table.getRowCount(); i++) 400 { 401 if (attrName.equalsIgnoreCase((String)table.getValueAt(i, 0))) 402 { 403 Object o = table.getValueAt(i, 1); 404 if (o instanceof String) 405 { 406 values.add((String)o); 407 } 408 } 409 } 410 return values; 411 } 412 413 private void updateAttributeVisibility() 414 { 415 tableModel.updateAttributeVisibility(); 416 } 417 418 /** {@inheritDoc} */ 419 protected List<Object> getValues(String attrName) 420 { 421 return tableModel.getValues(attrName); 422 } 423 424 /** The table model used by the tree in the panel. */ 425 protected class LDAPEntryTableModel extends SortableTableModel 426 implements Comparator<AttributeValuePair> 427 { 428 private static final long serialVersionUID = -1240282431326505113L; 429 private ArrayList<AttributeValuePair> dataArray = new ArrayList<>(); 430 private SortedSet<AttributeValuePair> allSortedValues = new TreeSet<>(this); 431 private Set<String> requiredAttrs = new HashSet<>(); 432 private final String[] COLUMN_NAMES = new String[] { 433 getHeader(LocalizableMessage.raw("Attribute"), 40), 434 getHeader(LocalizableMessage.raw("Value", 40))}; 435 private int sortColumn; 436 private boolean sortAscending = true; 437 438 /** 439 * Updates the contents of the table model with the 440 * {@code TableViewEntryPanel.searchResult} object. 441 */ 442 public void displayEntry() 443 { 444 updateDataArray(); 445 fireTableDataChanged(); 446 } 447 448 /** 449 * Updates the table model contents and sorts its contents depending on the 450 * sort options set by the user. 451 */ 452 public void forceResort() 453 { 454 updateDataArray(); 455 fireTableDataChanged(); 456 } 457 458 /** {@inheritDoc} */ 459 public int compare(AttributeValuePair desc1, AttributeValuePair desc2) 460 { 461 int result; 462 int[] possibleResults = { 463 desc1.attrName.compareTo(desc2.attrName), 464 compareValues(desc1.value, desc2.value)}; 465 result = possibleResults[sortColumn]; 466 if (result == 0) 467 { 468 for (int i : possibleResults) 469 { 470 if (i != 0) 471 { 472 result = i; 473 break; 474 } 475 } 476 } 477 if (!sortAscending) 478 { 479 result = -result; 480 } 481 return result; 482 } 483 484 private int compareValues(Object o1, Object o2) 485 { 486 if (o1 == null) 487 { 488 if (o2 == null) 489 { 490 return 0; 491 } 492 else 493 { 494 return -1; 495 } 496 } 497 else if (o2 == null) 498 { 499 return 1; 500 } 501 if (o1 instanceof ObjectClassValue) 502 { 503 o1 = renderer.getString((ObjectClassValue)o1); 504 } 505 else if (o1 instanceof BinaryValue) 506 { 507 o1 = renderer.getString((BinaryValue)o1); 508 } 509 else if (o1 instanceof byte[]) 510 { 511 o1 = renderer.getString((byte[])o1); 512 } 513 if (o2 instanceof ObjectClassValue) 514 { 515 o2 = renderer.getString((ObjectClassValue)o2); 516 } 517 else if (o2 instanceof BinaryValue) 518 { 519 o2 = renderer.getString((BinaryValue)o2); 520 } 521 else if (o2 instanceof byte[]) 522 { 523 o2 = renderer.getString((byte[])o2); 524 } 525 if (o1.getClass().equals(o2.getClass())) 526 { 527 if (o1 instanceof String) 528 { 529 return ((String)o1).compareTo((String)o2); 530 } 531 else if (o1 instanceof Integer) 532 { 533 return ((Integer)o1).compareTo((Integer)o2); 534 } 535 else if (o1 instanceof Long) 536 { 537 return ((Long)o1).compareTo((Long)o2); 538 } 539 else 540 { 541 return String.valueOf(o1).compareTo(String.valueOf(o2)); 542 } 543 } 544 else 545 { 546 return String.valueOf(o1).compareTo(String.valueOf(o2)); 547 } 548 } 549 550 /** {@inheritDoc} */ 551 public int getColumnCount() 552 { 553 return COLUMN_NAMES.length; 554 } 555 556 /** {@inheritDoc} */ 557 public int getRowCount() 558 { 559 return dataArray.size(); 560 } 561 562 /** {@inheritDoc} */ 563 public Object getValueAt(int row, int col) 564 { 565 if (col == 0) 566 { 567 return dataArray.get(row).attrName; 568 } 569 else 570 { 571 return dataArray.get(row).value; 572 } 573 } 574 575 /** {@inheritDoc} */ 576 public String getColumnName(int col) { 577 return COLUMN_NAMES[col]; 578 } 579 580 581 /** 582 * Returns whether the sort is ascending or descending. 583 * @return <CODE>true</CODE> if the sort is ascending and <CODE>false</CODE> 584 * otherwise. 585 */ 586 public boolean isSortAscending() 587 { 588 return sortAscending; 589 } 590 591 /** 592 * Sets whether to sort ascending of descending. 593 * @param sortAscending whether to sort ascending or descending. 594 */ 595 public void setSortAscending(boolean sortAscending) 596 { 597 this.sortAscending = sortAscending; 598 } 599 600 /** 601 * Returns the column index used to sort. 602 * @return the column index used to sort. 603 */ 604 public int getSortColumn() 605 { 606 return sortColumn; 607 } 608 609 /** 610 * Sets the column index used to sort. 611 * @param sortColumn column index used to sort.. 612 */ 613 public void setSortColumn(int sortColumn) 614 { 615 this.sortColumn = sortColumn; 616 } 617 618 /** {@inheritDoc} */ 619 public boolean isCellEditable(int row, int col) { 620 return col != 0 621 && !isReadOnly 622 && !schemaReadOnlyAttributesLowerCase.contains(dataArray.get(row).attrName.toLowerCase()); 623 } 624 625 /** {@inheritDoc} */ 626 public void setValueAt(Object value, int row, int col) 627 { 628 dataArray.get(row).value = value; 629 if (value instanceof ObjectClassValue) 630 { 631 updateObjectClass((ObjectClassValue)value); 632 } 633 else 634 { 635 fireTableCellUpdated(row, col); 636 637 notifyListeners(); 638 } 639 } 640 641 private void updateDataArray() 642 { 643 allSortedValues.clear(); 644 requiredAttrs.clear(); 645 List<String> addedAttrs = new ArrayList<>(); 646 Schema schema = getInfo().getServerDescriptor().getSchema(); 647 List<Object> ocs = null; 648 for (String attrName : searchResult.getAttributeNames()) 649 { 650 if (attrName.equalsIgnoreCase( 651 ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME)) 652 { 653 if (schema != null) 654 { 655 ocs = searchResult.getAttributeValues(attrName); 656 ObjectClassValue ocValue = getObjectClassDescriptor( 657 ocs, schema); 658 allSortedValues.add(new AttributeValuePair(attrName, ocValue)); 659 } 660 } 661 else 662 { 663 for (Object v : searchResult.getAttributeValues(attrName)) 664 { 665 allSortedValues.add(new AttributeValuePair(attrName, v)); 666 } 667 } 668 addedAttrs.add( 669 Utilities.getAttributeNameWithoutOptions(attrName).toLowerCase()); 670 } 671 if (ocs != null && schema != null) 672 { 673 for (Object o : ocs) 674 { 675 String oc = (String)o; 676 ObjectClass objectClass = schema.getObjectClass(oc.toLowerCase()); 677 if (objectClass != null) 678 { 679 for (AttributeType attr : objectClass.getRequiredAttributeChain()) 680 { 681 String attrName = attr.getNameOrOID(); 682 if (!addedAttrs.contains(attrName.toLowerCase())) 683 { 684 if (isBinary(attrName) || isPassword(attrName)) 685 { 686 allSortedValues.add(new AttributeValuePair(attrName, 687 new byte[]{})); 688 } 689 else 690 { 691 allSortedValues.add(new AttributeValuePair(attrName, "")); 692 } 693 } 694 requiredAttrs.add(attrName.toLowerCase()); 695 } 696 for (AttributeType attr : objectClass.getOptionalAttributeChain()) 697 { 698 String attrName = attr.getNameOrOID(); 699 if (!addedAttrs.contains(attrName.toLowerCase())) 700 { 701 if (isBinary(attrName) || isPassword(attrName)) 702 { 703 allSortedValues.add(new AttributeValuePair(attrName, 704 new byte[]{})); 705 } 706 else 707 { 708 allSortedValues.add(new AttributeValuePair(attrName, "")); 709 } 710 } 711 } 712 } 713 } 714 } 715 dataArray.clear(); 716 for (AttributeValuePair value : allSortedValues) 717 { 718 if (!showOnlyAttrsWithValues.isSelected() || 719 isRequired(value) || hasValue(value)) 720 { 721 dataArray.add(value); 722 } 723 } 724 renderer.setRequiredAttrs(requiredAttrs); 725 } 726 727 /** 728 * Checks if we have to display all the attributes or only those that 729 * contain a value and updates the contents of the model accordingly. Note 730 * that even if the required attributes have no value they will be 731 * displayed. 732 * 733 */ 734 void updateAttributeVisibility() 735 { 736 dataArray.clear(); 737 for (AttributeValuePair value : allSortedValues) 738 { 739 if (!showOnlyAttrsWithValues.isSelected() || 740 isRequired(value) || hasValue(value)) 741 { 742 dataArray.add(value); 743 } 744 } 745 fireTableDataChanged(); 746 747 Utilities.updateTableSizes(table); 748 Utilities.updateScrollMode(scroll, table); 749 } 750 751 /** 752 * Returns the list of values associated with a given attribute. 753 * @param attrName the name of the attribute. 754 * @return the list of values associated with a given attribute. 755 */ 756 public List<Object> getValues(String attrName) 757 { 758 List<Object> values = new ArrayList<>(); 759 for (AttributeValuePair valuePair : dataArray) 760 { 761 if (valuePair.attrName.equalsIgnoreCase(attrName) 762 && hasValue(valuePair)) 763 { 764 if (valuePair.value instanceof Collection<?>) 765 { 766 values.addAll((Collection<?>) valuePair.value); 767 } 768 else 769 { 770 values.add(valuePair.value); 771 } 772 } 773 } 774 return values; 775 } 776 777 private void updateObjectClass(ObjectClassValue newValue) 778 { 779 CustomSearchResult oldResult = searchResult; 780 CustomSearchResult newResult = 781 new CustomSearchResult(searchResult.getDN()); 782 783 for (String attrName : schemaReadOnlyAttributesLowerCase) 784 { 785 List<Object> values = searchResult.getAttributeValues(attrName); 786 if (!values.isEmpty()) 787 { 788 newResult.set(attrName, values); 789 } 790 } 791 ignoreEntryChangeEvents = true; 792 793 Schema schema = getInfo().getServerDescriptor().getSchema(); 794 if (schema != null) 795 { 796 ArrayList<String> attributes = new ArrayList<>(); 797 ArrayList<String> ocs = new ArrayList<>(); 798 if (newValue.getStructural() != null) 799 { 800 ocs.add(newValue.getStructural().toLowerCase()); 801 } 802 for (String oc : newValue.getAuxiliary()) 803 { 804 ocs.add(oc.toLowerCase()); 805 } 806 for (String oc : ocs) 807 { 808 ObjectClass objectClass = schema.getObjectClass(oc); 809 if (objectClass != null) 810 { 811 for (AttributeType attr : objectClass.getRequiredAttributeChain()) 812 { 813 attributes.add(attr.getNameOrOID().toLowerCase()); 814 } 815 for (AttributeType attr : objectClass.getOptionalAttributeChain()) 816 { 817 attributes.add(attr.getNameOrOID().toLowerCase()); 818 } 819 } 820 } 821 for (String attrName : editableOperationalAttrNames) 822 { 823 attributes.add(attrName.toLowerCase()); 824 } 825 for (AttributeValuePair currValue : allSortedValues) 826 { 827 String attrNoOptions = Utilities.getAttributeNameWithoutOptions( 828 currValue.attrName).toLowerCase(); 829 if (!attributes.contains(attrNoOptions)) 830 { 831 continue; 832 } 833 else if (!schemaReadOnlyAttributesLowerCase.contains( 834 currValue.attrName.toLowerCase())) 835 { 836 setValues(newResult, currValue.attrName); 837 } 838 } 839 } 840 update(newResult, isReadOnly, treePath); 841 ignoreEntryChangeEvents = false; 842 searchResult = oldResult; 843 notifyListeners(); 844 } 845 846 private boolean isRequired(AttributeValuePair value) 847 { 848 return requiredAttrs.contains( 849 Utilities.getAttributeNameWithoutOptions( 850 value.attrName.toLowerCase())); 851 } 852 853 private boolean hasValue(AttributeValuePair value) 854 { 855 boolean hasValue = value.value != null; 856 if (hasValue) 857 { 858 if (value.value instanceof String) 859 { 860 hasValue = ((String)value.value).length() > 0; 861 } 862 else if (value.value instanceof byte[]) 863 { 864 hasValue = ((byte[])value.value).length > 0; 865 } 866 } 867 return hasValue; 868 } 869 } 870 871 /** 872 * A simple class that contains an attribute name and a single value. It is 873 * used by the table model to be able to retrieve more easily all the values 874 * for a given attribute. 875 * 876 */ 877 class AttributeValuePair 878 { 879 /** 880 * The attribute name. 881 */ 882 String attrName; 883 /** 884 * The value. 885 */ 886 Object value; 887 /** 888 * Constructor. 889 * @param attrName the attribute name. 890 * @param value the value. 891 */ 892 public AttributeValuePair(String attrName, Object value) 893 { 894 this.attrName = attrName; 895 this.value = value; 896 } 897 } 898}