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