/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2010 Sun Microsystems, Inc. */ package org.opends.guitools.controlpanel.ui; import static org.opends.messages.AdminToolMessages.*; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.tree.TreePath; import org.opends.guitools.controlpanel.datamodel.BinaryValue; import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; import org.opends.guitools.controlpanel.datamodel.ObjectClassValue; import org.opends.guitools.controlpanel.datamodel.SortableTableModel; import org.opends.guitools.controlpanel.task.OnlineUpdateException; import org.opends.guitools.controlpanel.ui.renderer.AttributeCellEditor; import org.opends.guitools.controlpanel.ui.renderer.LDAPEntryTableCellRenderer; import org.opends.guitools.controlpanel.util.Utilities; import org.opends.messages.Message; import org.opends.server.types.*; import org.opends.server.util.LDIFReader; import org.opends.server.util.ServerConstants; /** * The panel displaying a table view of an LDAP entry. * */ public class TableViewEntryPanel extends ViewEntryPanel { private static final long serialVersionUID = 2135331526526472175L; private CustomSearchResult searchResult; private LDAPEntryTableModel tableModel; private LDAPEntryTableCellRenderer renderer; private JTable table; private boolean isReadOnly; private TreePath treePath; private JScrollPane scroll; private AttributeCellEditor editor; private JLabel requiredLabel; private JCheckBox showOnlyAttrsWithValues; /** * Default constructor. * */ public TableViewEntryPanel() { super(); createLayout(); } /** * {@inheritDoc} */ public Component getPreferredFocusComponent() { return table; } /** * Creates the layout of the panel (but the contents are not populated here). */ private void createLayout() { GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 1.0; addTitlePanel(this, gbc); gbc.gridy ++; gbc.insets.top = 5; gbc.gridwidth = 1; showOnlyAttrsWithValues = Utilities.createCheckBox( INFO_CTRL_PANEL_SHOW_ATTRS_WITH_VALUES_LABEL.get()); showOnlyAttrsWithValues.setSelected(displayOnlyWithAttrs); showOnlyAttrsWithValues.addActionListener(new ActionListener() { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent ev) { updateAttributeVisibility(); displayOnlyWithAttrs = showOnlyAttrsWithValues.isSelected(); } }); gbc.weightx = 0.0; gbc.anchor = GridBagConstraints.WEST; add(showOnlyAttrsWithValues, gbc); gbc.gridx ++; gbc.anchor = GridBagConstraints.EAST; gbc.fill = GridBagConstraints.NONE; requiredLabel = createRequiredLabel(); add(requiredLabel, gbc); gbc.insets = new Insets(0, 0, 0, 0); add(Box.createVerticalStrut(10), gbc); showOnlyAttrsWithValues.setFont(requiredLabel.getFont()); gbc.gridy ++; gbc.gridx = 0; gbc.insets.top = 10; gbc.gridwidth = 2; tableModel = new LDAPEntryTableModel(); renderer = new LDAPEntryTableCellRenderer(); table = Utilities.createSortableTable(tableModel, renderer); renderer.setTable(table); editor = new AttributeCellEditor(); table.getColumnModel().getColumn(1).setCellEditor(editor); gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; gbc.gridy ++; scroll = Utilities.createScrollPane(table); add(scroll, gbc); } /** * {@inheritDoc} */ public void update(CustomSearchResult sr, boolean isReadOnly, TreePath path) { boolean sameEntry = false; if (sr != null) { sr = filterSearchResult(sr); } if ((searchResult != null) && (sr != null)) { sameEntry = searchResult.getDN().equals(sr.getDN()); } searchResult = sr; final Point p = sameEntry ? scroll.getViewport().getViewPosition() : new Point(0, 0); renderer.setSchema(getInfo().getServerDescriptor().getSchema()); editor.setInfo(getInfo()); requiredLabel.setVisible(!isReadOnly); this.isReadOnly = isReadOnly; this.treePath = path; updateTitle(sr, path); ignoreEntryChangeEvents = true; tableModel.displayEntry(); Utilities.updateTableSizes(table); Utilities.updateScrollMode(scroll, table); SwingUtilities.invokeLater(new Runnable() { public void run() { if ((p != null) && (scroll.getViewport().contains(p))) { scroll.getViewport().setViewPosition(p); } ignoreEntryChangeEvents = false; } }); } /** * {@inheritDoc} */ public GenericDialog.ButtonType getButtonType() { return GenericDialog.ButtonType.NO_BUTTON; } /** * {@inheritDoc} */ public Entry getEntry() throws OpenDsException { if (SwingUtilities.isEventDispatchThread()) { editor.stopCellEditing(); } else { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { editor.stopCellEditing(); } }); } catch (Throwable t) { } } Entry entry = null; LDIFImportConfig ldifImportConfig = null; try { String ldif = getLDIF(); ldifImportConfig = new LDIFImportConfig(new StringReader(ldif)); LDIFReader reader = new LDIFReader(ldifImportConfig); entry = reader.readEntry(checkSchema()); addValuesInRDN(entry); } catch (IOException ioe) { throw new OnlineUpdateException( ERR_CTRL_PANEL_ERROR_CHECKING_ENTRY.get(ioe.toString()), ioe); } finally { if (ldifImportConfig != null) { ldifImportConfig.close(); } } return entry; } /** * Returns the LDIF representation of the displayed entry. * @return the LDIF representation of the displayed entry. */ private String getLDIF() { StringBuilder sb = new StringBuilder(); sb.append("dn: "+getDisplayedDN()); for (int i=0; i<tableModel.getRowCount(); i++) { String attrName = (String)tableModel.getValueAt(i, 0); if (!schemaReadOnlyAttributesLowerCase.contains(attrName.toLowerCase())) { Object value = tableModel.getValueAt(i, 1); appendLDIFLine(sb, attrName, value); } } return sb.toString(); } /** * {@inheritDoc} */ protected String getDisplayedDN() { StringBuilder sb = new StringBuilder(); try { DN oldDN = DN.decode(searchResult.getDN()); if (oldDN.getNumComponents() > 0) { RDN rdn = oldDN.getRDN(); List<AttributeType> attributeTypes = new ArrayList<AttributeType>(); List<String> attributeNames = new ArrayList<String>(); List<AttributeValue> attributeValues = new ArrayList<AttributeValue>(); for (int i=0; i<rdn.getNumValues(); i++) { String attrName = rdn.getAttributeName(i); AttributeValue value = rdn.getAttributeValue(i); String sValue = value.getValue().toString(); Set<String> values = getDisplayedStringValues(attrName); if (!values.contains(sValue)) { if (values.size() > 0) { String firstNonEmpty = null; for (String v : values) { v = v.trim(); if (v.length() > 0) { firstNonEmpty = v; break; } } if (firstNonEmpty != null) { AttributeType attr = rdn.getAttributeType(i); attributeTypes.add(attr); attributeNames.add(rdn.getAttributeName(i)); attributeValues.add(AttributeValues.create( attr, firstNonEmpty)); } } } else { attributeTypes.add(rdn.getAttributeType(i)); attributeNames.add(rdn.getAttributeName(i)); attributeValues.add(value); } } if (attributeTypes.size() == 0) { // Check the attributes in the order that we display them and use // the first one. Schema schema = getInfo().getServerDescriptor().getSchema(); if (schema != null) { for (int i=0; i<table.getRowCount(); i++) { String attrName = (String)table.getValueAt(i, 0); if (isPassword(attrName) || attrName.equals( ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME) || !table.isCellEditable(i, 1)) { continue; } Object o = table.getValueAt(i, 1); if (o instanceof String) { String aName = Utilities.getAttributeNameWithoutOptions(attrName); AttributeType attr = schema.getAttributeType(aName.toLowerCase()); if (attr != null) { attributeTypes.add(attr); attributeNames.add(attrName); attributeValues.add(AttributeValues.create( attr, (String)o)); } break; } } } } DN parent = oldDN.getParent(); if (attributeTypes.size() > 0) { DN newDN; RDN newRDN = new RDN(attributeTypes, attributeNames, attributeValues); if (parent == null) { newDN = new DN(new RDN[]{newRDN}); } else { newDN = parent.concat(newRDN); } sb.append(newDN.toString()); } else { if (parent != null) { sb.append(","+parent.toString()); } } } } catch (Throwable t) { throw new RuntimeException("Unexpected error: "+t, t); } return sb.toString(); } private Set<String> getDisplayedStringValues(String attrName) { Set<String> values = new LinkedHashSet<String>(); for (int i=0; i<table.getRowCount(); i++) { if (attrName.equalsIgnoreCase((String)table.getValueAt(i, 0))) { Object o = table.getValueAt(i, 1); if (o instanceof String) { values.add((String)o); } } } return values; } private void updateAttributeVisibility() { tableModel.updateAttributeVisibility(); } /** * {@inheritDoc} */ protected List<Object> getValues(String attrName) { return tableModel.getValues(attrName); } /** * The table model used by the tree in the panel. * */ protected class LDAPEntryTableModel extends SortableTableModel implements Comparator<AttributeValuePair> { private static final long serialVersionUID = -1240282431326505113L; private ArrayList<AttributeValuePair> dataArray = new ArrayList<AttributeValuePair>(); private SortedSet<AttributeValuePair> allSortedValues = new TreeSet<AttributeValuePair>(this); Set<String> requiredAttrs = new HashSet<String>(); private final String[] COLUMN_NAMES = new String[] { getHeader(Message.raw("Attribute"), 40), getHeader(Message.raw("Value", 40))}; private int sortColumn = 0; private boolean sortAscending = true; /** * Updates the contents of the table model with the * {@code TableViewEntryPanel.searchResult} object. */ public void displayEntry() { updateDataArray(); fireTableDataChanged(); } /** * Updates the table model contents and sorts its contents depending on the * sort options set by the user. */ public void forceResort() { updateDataArray(); fireTableDataChanged(); } /** * {@inheritDoc} */ public int compare(AttributeValuePair desc1, AttributeValuePair desc2) { int result; int[] possibleResults = { desc1.attrName.compareTo(desc2.attrName), compareValues(desc1.value, desc2.value)}; result = possibleResults[sortColumn]; if (result == 0) { for (int i : possibleResults) { if (i != 0) { result = i; break; } } } if (!sortAscending) { result = -result; } return result; } private int compareValues(Object o1, Object o2) { if (o1 == null) { if (o2 == null) { return 0; } else { return -1; } } else if (o2 == null) { return 1; } if (o1 instanceof ObjectClassValue) { o1 = renderer.getString((ObjectClassValue)o1); } else if (o1 instanceof BinaryValue) { o1 = renderer.getString((BinaryValue)o1); } else if (o1 instanceof byte[]) { o1 = renderer.getString((byte[])o1); } if (o2 instanceof ObjectClassValue) { o2 = renderer.getString((ObjectClassValue)o2); } else if (o2 instanceof BinaryValue) { o2 = renderer.getString((BinaryValue)o2); } else if (o2 instanceof byte[]) { o2 = renderer.getString((byte[])o2); } if (o1.getClass().equals(o2.getClass())) { if (o1 instanceof String) { return ((String)o1).compareTo((String)o2); } else if (o1 instanceof Integer) { return ((Integer)o1).compareTo((Integer)o2); } else if (o1 instanceof Long) { return ((Long)o1).compareTo((Long)o2); } else { return String.valueOf(o1).compareTo(String.valueOf(o2)); } } else { return String.valueOf(o1).compareTo(String.valueOf(o2)); } } /** * {@inheritDoc} */ public int getColumnCount() { return COLUMN_NAMES.length; } /** * {@inheritDoc} */ public int getRowCount() { return dataArray.size(); } /** * {@inheritDoc} */ public Object getValueAt(int row, int col) { if (col == 0) { return dataArray.get(row).attrName; } else { return dataArray.get(row).value; } } /** * {@inheritDoc} */ public String getColumnName(int col) { return COLUMN_NAMES[col]; } /** * Returns whether the sort is ascending or descending. * @return <CODE>true</CODE> if the sort is ascending and <CODE>false</CODE> * otherwise. */ public boolean isSortAscending() { return sortAscending; } /** * Sets whether to sort ascending of descending. * @param sortAscending whether to sort ascending or descending. */ public void setSortAscending(boolean sortAscending) { this.sortAscending = sortAscending; } /** * Returns the column index used to sort. * @return the column index used to sort. */ public int getSortColumn() { return sortColumn; } /** * Sets the column index used to sort. * @param sortColumn column index used to sort.. */ public void setSortColumn(int sortColumn) { this.sortColumn = sortColumn; } /** * {@inheritDoc} */ public boolean isCellEditable(int row, int col) { if (col == 0) { return false; } else { if (!isReadOnly) { return !schemaReadOnlyAttributesLowerCase.contains( dataArray.get(row).attrName.toLowerCase()); } else { return false; } } } /** * {@inheritDoc} */ public void setValueAt(Object value, int row, int col) { dataArray.get(row).value = value; if (value instanceof ObjectClassValue) { updateObjectClass((ObjectClassValue)value); } else { fireTableCellUpdated(row, col); notifyListeners(); } } private void updateDataArray() { allSortedValues.clear(); requiredAttrs.clear(); List<String> addedAttrs = new ArrayList<String>(); Schema schema = getInfo().getServerDescriptor().getSchema(); List<Object> ocs = null; for (String attrName : searchResult.getAttributeNames()) { if (attrName.equalsIgnoreCase( ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME)) { if (schema != null) { ocs = searchResult.getAttributeValues(attrName); ObjectClassValue ocValue = getObjectClassDescriptor( ocs, schema); allSortedValues.add(new AttributeValuePair(attrName, ocValue)); } } else { for (Object v : searchResult.getAttributeValues(attrName)) { allSortedValues.add(new AttributeValuePair(attrName, v)); } } addedAttrs.add( Utilities.getAttributeNameWithoutOptions(attrName).toLowerCase()); } if ((ocs != null) && (schema != null)) { for (Object o : ocs) { String oc = (String)o; ObjectClass objectClass = schema.getObjectClass(oc.toLowerCase()); if (objectClass != null) { for (AttributeType attr : objectClass.getRequiredAttributeChain()) { String attrName = attr.getNameOrOID(); if (!addedAttrs.contains(attrName.toLowerCase())) { if (isBinary(attrName) || isPassword(attrName)) { allSortedValues.add(new AttributeValuePair(attrName, new byte[]{})); } else { allSortedValues.add(new AttributeValuePair(attrName, "")); } } requiredAttrs.add(attrName.toLowerCase()); } for (AttributeType attr : objectClass.getOptionalAttributeChain()) { String attrName = attr.getNameOrOID(); if (!addedAttrs.contains(attrName.toLowerCase())) { if (isBinary(attrName) || isPassword(attrName)) { allSortedValues.add(new AttributeValuePair(attrName, new byte[]{})); } else { allSortedValues.add(new AttributeValuePair(attrName, "")); } } } } } } dataArray.clear(); for (AttributeValuePair value : allSortedValues) { if (!showOnlyAttrsWithValues.isSelected() || isRequired(value) || hasValue(value)) { dataArray.add(value); } } renderer.setRequiredAttrs(requiredAttrs); } /** * Checks if we have to display all the attributes or only those that * contain a value and updates the contents of the model accordingly. Note * that even if the required attributes have no value they will be * displayed. * */ void updateAttributeVisibility() { dataArray.clear(); for (AttributeValuePair value : allSortedValues) { if (!showOnlyAttrsWithValues.isSelected() || isRequired(value) || hasValue(value)) { dataArray.add(value); } } fireTableDataChanged(); Utilities.updateTableSizes(table); Utilities.updateScrollMode(scroll, table); } /** * Returns the list of values associated with a given attribute. * @param attrName the name of the attribute. * @return the list of values associated with a given attribute. */ public List<Object> getValues(String attrName) { List<Object> values = new ArrayList<Object>(); for (AttributeValuePair valuePair : dataArray) { if (valuePair.attrName.equalsIgnoreCase(attrName)) { if (hasValue(valuePair)) { if (valuePair.value instanceof Collection<?>) { for (Object o : (Collection<?>)valuePair.value) { values.add(o); } } else { values.add(valuePair.value); } } } } return values; } private void updateObjectClass(ObjectClassValue newValue) { CustomSearchResult oldResult = searchResult; CustomSearchResult newResult = new CustomSearchResult(searchResult.getDN()); for (String attrName : schemaReadOnlyAttributesLowerCase) { List<Object> values = searchResult.getAttributeValues(attrName); if (!values.isEmpty()) { newResult.set(attrName, values); } } ignoreEntryChangeEvents = true; Schema schema = getInfo().getServerDescriptor().getSchema(); if (schema != null) { ArrayList<String> attributes = new ArrayList<String>(); ArrayList<String> ocs = new ArrayList<String>(); if (newValue.getStructural() != null) { ocs.add(newValue.getStructural().toLowerCase()); } for (String oc : newValue.getAuxiliary()) { ocs.add(oc.toLowerCase()); } for (String oc : ocs) { ObjectClass objectClass = schema.getObjectClass(oc); if (objectClass != null) { for (AttributeType attr : objectClass.getRequiredAttributeChain()) { attributes.add(attr.getNameOrOID().toLowerCase()); } for (AttributeType attr : objectClass.getOptionalAttributeChain()) { attributes.add(attr.getNameOrOID().toLowerCase()); } } } for (String attrName : editableOperationalAttrNames) { attributes.add(attrName.toLowerCase()); } for (AttributeValuePair currValue : allSortedValues) { String attrNoOptions = Utilities.getAttributeNameWithoutOptions( currValue.attrName).toLowerCase(); if (!attributes.contains(attrNoOptions)) { continue; } else if (!schemaReadOnlyAttributesLowerCase.contains( currValue.attrName.toLowerCase())) { setValues(newResult, currValue.attrName); } } } update(newResult, isReadOnly, treePath); ignoreEntryChangeEvents = false; searchResult = oldResult; notifyListeners(); } private boolean isRequired(AttributeValuePair value) { return requiredAttrs.contains( Utilities.getAttributeNameWithoutOptions( value.attrName.toLowerCase())); } private boolean hasValue(AttributeValuePair value) { boolean hasValue = value.value != null; if (hasValue) { if (value.value instanceof String) { hasValue = ((String)value.value).length() > 0; } else if (value.value instanceof byte[]) { hasValue = ((byte[])value.value).length > 0; } } return hasValue; } } /** * A simple class that contains an attribute name and a single value. It is * used by the table model to be able to retrieve more easily all the values * for a given attribute. * */ class AttributeValuePair { /** * The attribute name. */ String attrName; /** * The value. */ Object value; /** * Constructor. * @param attrName the attribute name. * @param value the value. */ public AttributeValuePair(String attrName, Object value) { this.attrName = attrName; this.value = value; } } }