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}