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 2011-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.datamodel;
028
029import static org.opends.server.util.StaticUtils.*;
030
031import java.util.ArrayList;
032import java.util.Collections;
033import java.util.HashMap;
034import java.util.List;
035import java.util.Map;
036import java.util.SortedSet;
037import java.util.TreeSet;
038
039import javax.naming.CompositeName;
040import javax.naming.Name;
041import javax.naming.NamingEnumeration;
042import javax.naming.NamingException;
043import javax.naming.directory.Attribute;
044import javax.naming.directory.Attributes;
045import javax.naming.directory.SearchResult;
046
047import org.forgerock.opendj.ldap.ByteString;
048import org.opends.guitools.controlpanel.util.Utilities;
049import org.opends.server.core.DirectoryServer;
050import org.opends.server.types.AttributeBuilder;
051import org.opends.server.types.AttributeType;
052import org.opends.server.types.DN;
053import org.opends.server.types.Entry;
054import org.opends.server.types.ObjectClass;
055import org.opends.server.types.OpenDsException;
056import org.opends.server.util.LDIFReader;
057
058/**
059 * This is a commodity class used to wrap the SearchResult class of JNDI.
060 * Basically it retrieves all the attributes and values on the SearchResult and
061 * calculates its DN.  Using it we avoid having to handle the NamingException
062 * exceptions that most of the methods in SearchResult throw.
063 */
064public class CustomSearchResult implements Comparable<CustomSearchResult>
065{
066  private String dn;
067  private Map<String, List<Object>> attributes;
068  private SortedSet<String> attrNames;
069  private String toString;
070  private int hashCode;
071
072  /**
073   * Constructor of an empty search result.  This constructor is used by the
074   * LDAP entry editor which 'build' their own CustomSearchResult.  The entry
075   * editors use some methods that require CustomSearchResult.
076   * @param dn the dn of the entry.
077   */
078  public CustomSearchResult(String dn)
079  {
080    this.dn = dn;
081    attributes = new HashMap<>();
082    attrNames = new TreeSet<>();
083    toString = calculateToString();
084    hashCode = calculateHashCode();
085  }
086
087  /**
088   * Constructor of a search result using a SearchResult as basis.
089   * @param sr the SearchResult.
090   * @param baseDN the base DN of the search that returned the SearchResult.
091   * @throws NamingException if there is an error retrieving the attribute
092   * values.
093   */
094  public CustomSearchResult(SearchResult sr, String baseDN)
095  throws NamingException
096  {
097    String sName = sr.getName();
098    Name name;
099    if (baseDN != null && baseDN.length() > 0)
100    {
101      if (sName != null && sName.length() > 0)
102      {
103        name = new CompositeName(sName);
104        name.add(baseDN);
105
106      }
107      else {
108        name = Utilities.getJNDIName(baseDN);
109      }
110    }
111    else {
112      name = new CompositeName(sName);
113    }
114    StringBuilder buf = new StringBuilder();
115    for (int i=0; i<name.size(); i++)
116    {
117      String n = name.get(i);
118      if (n != null && n.length() > 0)
119      {
120        if (buf.length() != 0)
121        {
122          buf.append(",");
123        }
124        buf.append(n);
125      }
126    }
127    dn = buf.toString();
128
129    attributes = new HashMap<>();
130    attrNames = new TreeSet<>();
131    Attributes attrs = sr.getAttributes();
132    if (attrs != null)
133    {
134      NamingEnumeration<?> en = attrs.getAll();
135      try
136      {
137        while (en.hasMore()) {
138          Attribute attr = (Attribute)en.next();
139          String attrName = attr.getID();
140          attrNames.add(attrName);
141          List<Object> values = new ArrayList<>();
142          for (int i=0; i<attr.size(); i++)
143          {
144            Object v = attr.get(i);
145            if (!"".equals(v.toString()))
146            {
147              values.add(v);
148            }
149          }
150          attributes.put(attrName.toLowerCase(), values);
151        }
152      }
153      finally
154      {
155        en.close();
156      }
157    }
158    toString = calculateToString();
159    hashCode = calculateHashCode();
160  }
161
162  /**
163   * Returns the DN of the entry.
164   * @return the DN of the entry.
165   */
166  public String getDN() {
167    return dn;
168  }
169
170  /**
171   * Returns the values for a given attribute.  It returns an empty Set if
172   * the attribute is not defined.
173   * @param name the name of the attribute.
174   * @return the values for a given attribute.  It returns an empty Set if
175   * the attribute is not defined.
176   */
177  public List<Object> getAttributeValues(String name) {
178    List<Object> values = attributes.get(name.toLowerCase());
179    if (values == null)
180    {
181      values = Collections.emptyList();
182    }
183    return values;
184  }
185
186  /**
187   * Returns all the attribute names of the entry.
188   * @return the attribute names of the entry.
189   */
190  public SortedSet<String> getAttributeNames() {
191    return attrNames;
192  }
193
194  /** {@inheritDoc} */
195  public int compareTo(CustomSearchResult o) {
196    if (this.equals(o))
197    {
198      return 0;
199    }
200    return toString().compareTo(o.toString());
201  }
202
203  /**
204   * Return a new object, copy of the current object.
205   *
206   * @return a new object, copy of the current object
207   */
208  public CustomSearchResult duplicate()
209  {
210    CustomSearchResult sr = new CustomSearchResult(dn);
211    sr.attributes = new HashMap<>(attributes);
212    sr.attrNames = new TreeSet<>(attrNames);
213    sr.toString = toString;
214    sr.hashCode = hashCode;
215    return sr;
216  }
217
218  /** {@inheritDoc} */
219  public boolean equals(Object o)
220  {
221    if (o == this)
222    {
223      return true;
224    }
225    if (o instanceof CustomSearchResult)
226    {
227      CustomSearchResult sr = (CustomSearchResult)o;
228      return getDN().equals(sr.getDN())
229          && getAttributeNames().equals(sr.getAttributeNames())
230          && attrValuesEqual(sr);
231    }
232    return false;
233  }
234
235  private boolean attrValuesEqual(CustomSearchResult sr)
236  {
237    for (String attrName : getAttributeNames())
238    {
239      if (!getAttributeValues(attrName).equals(sr.getAttributeValues(attrName)))
240      {
241        return false;
242      }
243    }
244    return true;
245  }
246
247  /** {@inheritDoc} */
248  public String toString() {
249    return toString;
250  }
251
252  /** {@inheritDoc} */
253  public int hashCode() {
254    return hashCode;
255  }
256
257  /**
258   * Sets the values for a given attribute name.
259   * @param attrName the name of the attribute.
260   * @param values the values for the attribute.
261   */
262  public void set(String attrName, List<Object> values)
263  {
264    attrNames.add(attrName);
265    attrName = attrName.toLowerCase();
266    attributes.put(attrName, values);
267    toString = calculateToString();
268    hashCode = calculateHashCode();
269  }
270
271  private String calculateToString()
272  {
273    return "dn: "+dn+"\nattributes: "+attributes;
274  }
275
276  private int calculateHashCode()
277  {
278    return 23 + toString.hashCode();
279  }
280
281  /**
282   * Gets the Entry object equivalent to this CustomSearchResult.
283   * The method assumes that the schema in DirectoryServer has been initialized.
284   * @return the Entry object equivalent to this CustomSearchResult.
285   * @throws OpenDsException if there is an error parsing the DN or retrieving
286   * the attributes definition and objectclasses in the schema of the server.
287   */
288  public Entry getEntry() throws OpenDsException
289  {
290    DN dn = DN.valueOf(getDN());
291    Map<ObjectClass,String> objectClasses = new HashMap<>();
292    Map<AttributeType,List<org.opends.server.types.Attribute>> userAttributes = new HashMap<>();
293    Map<AttributeType,List<org.opends.server.types.Attribute>> operationalAttributes = new HashMap<>();
294
295    for (String wholeName : getAttributeNames())
296    {
297      final org.opends.server.types.Attribute attribute =
298        LDIFReader.parseAttrDescription(wholeName);
299      final String attrName = attribute.getName();
300      final String lowerName = toLowerCase(attrName);
301
302      // See if this is an objectclass or an attribute.  Then get the
303      // corresponding definition and add the value to the appropriate hash.
304      if (lowerName.equals("objectclass"))
305      {
306        for (Object value : getAttributeValues(attrName))
307        {
308          String ocName = value.toString().trim();
309          String lowerOCName = toLowerCase(ocName);
310
311          ObjectClass objectClass =
312            DirectoryServer.getObjectClass(lowerOCName);
313          if (objectClass == null)
314          {
315            objectClass = DirectoryServer.getDefaultObjectClass(ocName);
316          }
317
318          objectClasses.put(objectClass, ocName);
319        }
320      }
321      else
322      {
323        AttributeType attrType = DirectoryServer.getAttributeTypeOrDefault(lowerName, attrName);
324        AttributeBuilder builder = new AttributeBuilder(attribute, true);
325        for (Object value : getAttributeValues(attrName))
326        {
327          ByteString bs;
328          if (value instanceof byte[])
329          {
330            bs = ByteString.wrap((byte[])value);
331          }
332          else
333          {
334            bs = ByteString.valueOfUtf8(value.toString());
335          }
336          builder.add(bs);
337        }
338
339        List<org.opends.server.types.Attribute> attrList = builder.toAttributeList();
340        if (attrType.isOperational())
341        {
342          operationalAttributes.put(attrType, attrList);
343        }
344        else
345        {
346          userAttributes.put(attrType, attrList);
347        }
348      }
349    }
350
351    return new Entry(dn, objectClasses, userAttributes, operationalAttributes);
352  }
353}