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