/*
* 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 legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* 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 legal-notices/CDDLv1_0.txt.
* 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.
* Portions Copyright 2011-2015 ForgeRock AS
*/
package org.opends.guitools.controlpanel.datamodel;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.naming.CompositeName;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchResult;
import org.forgerock.opendj.ldap.ByteString;
import org.opends.guitools.controlpanel.util.Utilities;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.AttributeBuilder;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.OpenDsException;
import org.opends.server.util.LDIFReader;
/**
* This is a commodity class used to wrap the SearchResult class of JNDI.
* Basically it retrieves all the attributes and values on the SearchResult and
* calculates its DN. Using it we avoid having to handle the NamingException
* exceptions that most of the methods in SearchResult throw.
*/
public class CustomSearchResult implements Comparable<CustomSearchResult>
{
private String dn;
private Map<String, List<Object>> attributes;
private SortedSet<String> attrNames;
private String toString;
private int hashCode;
/**
* Constructor of an empty search result. This constructor is used by the
* LDAP entry editor which 'build' their own CustomSearchResult. The entry
* editors use some methods that require CustomSearchResult.
* @param dn the dn of the entry.
*/
public CustomSearchResult(String dn)
{
this.dn = dn;
attributes = new HashMap<>();
attrNames = new TreeSet<>();
toString = calculateToString();
hashCode = calculateHashCode();
}
/**
* Constructor of a search result using a SearchResult as basis.
* @param sr the SearchResult.
* @param baseDN the base DN of the search that returned the SearchResult.
* @throws NamingException if there is an error retrieving the attribute
* values.
*/
public CustomSearchResult(SearchResult sr, String baseDN)
throws NamingException
{
String sName = sr.getName();
Name name;
if (baseDN != null && baseDN.length() > 0)
{
if (sName != null && sName.length() > 0)
{
name = new CompositeName(sName);
name.add(baseDN);
}
else {
name = Utilities.getJNDIName(baseDN);
}
}
else {
name = new CompositeName(sName);
}
StringBuilder buf = new StringBuilder();
for (int i=0; i<name.size(); i++)
{
String n = name.get(i);
if (n != null && n.length() > 0)
{
if (buf.length() != 0)
{
buf.append(",");
}
buf.append(n);
}
}
dn = buf.toString();
attributes = new HashMap<>();
attrNames = new TreeSet<>();
Attributes attrs = sr.getAttributes();
if (attrs != null)
{
NamingEnumeration<?> en = attrs.getAll();
try
{
while (en.hasMore()) {
Attribute attr = (Attribute)en.next();
String attrName = attr.getID();
attrNames.add(attrName);
List<Object> values = new ArrayList<>();
for (int i=0; i<attr.size(); i++)
{
Object v = attr.get(i);
if (!"".equals(v.toString()))
{
values.add(v);
}
}
attributes.put(attrName.toLowerCase(), values);
}
}
finally
{
en.close();
}
}
toString = calculateToString();
hashCode = calculateHashCode();
}
/**
* Returns the DN of the entry.
* @return the DN of the entry.
*/
public String getDN() {
return dn;
}
/**
* Returns the values for a given attribute. It returns an empty Set if
* the attribute is not defined.
* @param name the name of the attribute.
* @return the values for a given attribute. It returns an empty Set if
* the attribute is not defined.
*/
public List<Object> getAttributeValues(String name) {
List<Object> values = attributes.get(name.toLowerCase());
if (values == null)
{
values = Collections.emptyList();
}
return values;
}
/**
* Returns all the attribute names of the entry.
* @return the attribute names of the entry.
*/
public SortedSet<String> getAttributeNames() {
return attrNames;
}
/** {@inheritDoc} */
public int compareTo(CustomSearchResult o) {
if (this.equals(o))
{
return 0;
}
return toString().compareTo(o.toString());
}
/**
* Return a new object, copy of the current object.
*
* @return a new object, copy of the current object
*/
public CustomSearchResult duplicate()
{
CustomSearchResult sr = new CustomSearchResult(dn);
sr.attributes = new HashMap<>(attributes);
sr.attrNames = new TreeSet<>(attrNames);
sr.toString = toString;
sr.hashCode = hashCode;
return sr;
}
/** {@inheritDoc} */
public boolean equals(Object o)
{
if (o == this)
{
return true;
}
if (o instanceof CustomSearchResult)
{
CustomSearchResult sr = (CustomSearchResult)o;
return getDN().equals(sr.getDN())
&& getAttributeNames().equals(sr.getAttributeNames())
&& attrValuesEqual(sr);
}
return false;
}
private boolean attrValuesEqual(CustomSearchResult sr)
{
for (String attrName : getAttributeNames())
{
if (!getAttributeValues(attrName).equals(sr.getAttributeValues(attrName)))
{
return false;
}
}
return true;
}
/** {@inheritDoc} */
public String toString() {
return toString;
}
/** {@inheritDoc} */
public int hashCode() {
return hashCode;
}
/**
* Sets the values for a given attribute name.
* @param attrName the name of the attribute.
* @param values the values for the attribute.
*/
public void set(String attrName, List<Object> values)
{
attrNames.add(attrName);
attrName = attrName.toLowerCase();
attributes.put(attrName, values);
toString = calculateToString();
hashCode = calculateHashCode();
}
private String calculateToString()
{
return "dn: "+dn+"\nattributes: "+attributes;
}
private int calculateHashCode()
{
return 23 + toString.hashCode();
}
/**
* Gets the Entry object equivalent to this CustomSearchResult.
* The method assumes that the schema in DirectoryServer has been initialized.
* @return the Entry object equivalent to this CustomSearchResult.
* @throws OpenDsException if there is an error parsing the DN or retrieving
* the attributes definition and objectclasses in the schema of the server.
*/
public Entry getEntry() throws OpenDsException
{
DN dn = DN.valueOf(getDN());
Map<ObjectClass,String> objectClasses = new HashMap<>();
Map<AttributeType,List<org.opends.server.types.Attribute>> userAttributes = new HashMap<>();
Map<AttributeType,List<org.opends.server.types.Attribute>> operationalAttributes = new HashMap<>();
for (String wholeName : getAttributeNames())
{
final org.opends.server.types.Attribute attribute =
LDIFReader.parseAttrDescription(wholeName);
final String attrName = attribute.getName();
final String lowerName = toLowerCase(attrName);
// See if this is an objectclass or an attribute. Then get the
// corresponding definition and add the value to the appropriate hash.
if (lowerName.equals("objectclass"))
{
for (Object value : getAttributeValues(attrName))
{
String ocName = value.toString().trim();
String lowerOCName = toLowerCase(ocName);
ObjectClass objectClass =
DirectoryServer.getObjectClass(lowerOCName);
if (objectClass == null)
{
objectClass = DirectoryServer.getDefaultObjectClass(ocName);
}
objectClasses.put(objectClass, ocName);
}
}
else
{
AttributeType attrType = DirectoryServer.getAttributeTypeOrDefault(lowerName, attrName);
AttributeBuilder builder = new AttributeBuilder(attribute, true);
for (Object value : getAttributeValues(attrName))
{
ByteString bs;
if (value instanceof byte[])
{
bs = ByteString.wrap((byte[])value);
}
else
{
bs = ByteString.valueOfUtf8(value.toString());
}
builder.add(bs);
}
List<org.opends.server.types.Attribute> attrList = builder.toAttributeList();
if (attrType.isOperational())
{
operationalAttributes.put(attrType, attrList);
}
else
{
userAttributes.put(attrType, attrList);
}
}
}
return new Entry(dn, objectClasses, userAttributes, operationalAttributes);
}
}