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}