001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: AMIdentityRepository.java,v 1.21 2010/01/06 01:58:26 veiming Exp $
026 *
027 */
028
029/*
030 * Portions Copyrighted [2011] [ForgeRock AS]
031 */
032package com.sun.identity.idm;
033
034import java.util.ArrayList;
035import java.util.Collections;
036import java.util.HashMap;
037import java.util.HashSet;
038import java.util.Iterator;
039import java.util.Map;
040import java.util.Set;
041
042import javax.security.auth.callback.Callback;
043
044import com.sun.identity.shared.ldap.LDAPDN;
045import com.sun.identity.shared.ldap.util.DN;
046
047import com.iplanet.am.sdk.AMHashMap;
048import com.iplanet.sso.SSOException;
049import com.iplanet.sso.SSOToken;
050import com.sun.identity.common.CaseInsensitiveHashMap;
051import com.sun.identity.common.DNUtils;
052import com.sun.identity.shared.debug.Debug;
053import com.sun.identity.sm.DNMapper;
054import com.sun.identity.sm.OrganizationConfigManager;
055import com.sun.identity.sm.SMSException;
056
057/**
058 * The class <code> AMIdentityRepository </code> represents an object to access
059 * the repositories in which user/role/group and other identity data is
060 * configured. This class provides access to methods which will search, create
061 * and delete identities. An instance of this class can be obtained in the
062 * following manner:
063 * <p>
064 * 
065 * <PRE>
066 * 
067 * AMIdentityRepository idRepo = new AMIdentityRepository(ssoToken, realmName);
068 * 
069 * </PRE>
070 * 
071 * @supported.api
072 */
073public final class AMIdentityRepository {
074    private SSOToken token;
075    private String organizationDN;
076    private String idRealmName;
077
078    public static Debug debug = Debug.getInstance("amIdm");
079    public static Map listeners = new CaseInsensitiveHashMap();
080
081    /**
082     * @supported.api
083     * 
084     * Constructor for the <code>AMIdentityRepository</code> object. If a null
085     * is passed for the organization identifier <code>realmName</code>, then
086     * the "root" realm is assumed.
087     * 
088     * @param ssotoken
089     *            Single sign on token of the user
090     * @param realmName
091     *            Name of the realm (can be a Fully qualified DN)
092     * @throws IdRepoException
093     *             if there are repository related error conditions.
094     * @throws SSOException
095     *             if user's single sign on token is invalid.
096     */
097    public AMIdentityRepository(SSOToken ssotoken, String realmName)
098            throws IdRepoException, SSOException {
099        token = ssotoken;
100        idRealmName = realmName;
101        organizationDN = DNMapper.orgNameToDN(realmName);
102    }
103
104    /**
105     * @supported.api
106     * 
107     * Returns the set of supported object types <code>IdType</code> for this
108     * deployment. This is not realm specific.
109     * 
110     * @return Set of supported <code> IdType </code> objects.
111     * @throws IdRepoException
112     *             if there are repository related error conditions.
113     * @throws SSOException
114     *             if user's single sign on token is invalid.
115     */
116    public Set getSupportedIdTypes() throws IdRepoException, SSOException {
117        IdServices idServices = IdServicesFactory.getDataStoreServices();
118        Set res = idServices.getSupportedTypes(token, organizationDN);
119        res.remove(IdType.REALM);
120        return res;
121    }
122
123    /**
124     * @supported.api
125     * 
126     * Returns the set of Operations for a given <code>IdType</code>,
127     * <code>IdOperations</code> that can be performed on an Identity. This
128     * varies for each organization (and each plugin?).
129     * 
130     * @param type
131     *            Type of identity
132     * @return Set of <code>IdOperation</code> objects.
133     * @throws IdRepoException
134     *             if there are repository related error conditions.
135     * @throws SSOException
136     *             if user's single sign on token is invalid.
137     */
138    public Set getAllowedIdOperations(IdType type) throws IdRepoException,
139            SSOException {
140        IdServices idServices = IdServicesFactory.getDataStoreServices();
141        return idServices.getSupportedOperations(token, type, organizationDN);
142
143    }
144
145    /**
146     * 
147     * Return the special identities for this realm for a given type. These
148     * identities cannot be deleted and hence have to be shown in the admin
149     * console as non-deletable.
150     * 
151     * @param type
152     *            Type of the identity
153     * @return IdSearchResult
154     * @throws IdRepoException
155     *             if there is a datastore exception
156     * @throws SSOException
157     *             if the user's single sign on token is not valid.
158     */
159    public IdSearchResults getSpecialIdentities(IdType type)
160            throws IdRepoException, SSOException {
161
162        IdSearchResults results = getSpecialIdentities(token, type, 
163                organizationDN);
164
165        if (type.equals(IdType.USER)) {
166            // Iterating through to get out the names and remove only amadmin
167            // anonymous as per AM console requirement.
168
169            IdSearchResults newResults = new IdSearchResults(type, 
170                    organizationDN);
171            Set identities = results.getSearchResults();
172            if ((identities != null) && !identities.isEmpty()) {
173                for (Iterator i = identities.iterator(); i.hasNext();) {
174                    AMIdentity amid = ((AMIdentity) i.next());
175                    String remUser = amid.getName().toLowerCase();
176                    if (!remUser.equalsIgnoreCase(IdConstants.AMADMIN_USER)
177                            && !remUser.equalsIgnoreCase(
178                                    IdConstants.ANONYMOUS_USER)) 
179                    {
180                        newResults.addResult(amid, Collections.EMPTY_MAP);
181                    }
182                }
183                results = newResults;
184            }
185        }
186        return results;
187    }
188
189    /**
190     * Searches for identities of a certain type. The iterator returns
191     * AMIdentity objects for use by the application.
192     * 
193     * @deprecated This method is deprecated. Use
194     *             {@link #searchIdentities(IdType type,String pattern,
195     *             IdSearchControl ctrl)}
196     * @param type
197     *            Type of identity being searched for.
198     * @param pattern
199     *            Search pattern, like "a*" or "*".
200     * @param avPairs
201     *            Map of attribute-values which can further help qualify the
202     *            search pattern.
203     * @param recursive
204     *            If true, then the search is performed on the entire subtree
205     *            (if applicable)
206     * @param maxResults
207     *            Maximum number of results to be returned. A -1 means no limit
208     *            on the result set.
209     * @param maxTime
210     *            Maximum amount of time after which the search should return
211     *            with partial results.
212     * @param returnAttributes
213     *            Set of attributes to be read when performing the search.
214     * @param returnAllAttributes
215     *            If true, then read all the attributes of the entries.
216     * @return results containing <code>AMIdentity</code> objects.
217     * @throws IdRepoException
218     *             if there are repository related error conditions.
219     * @throws SSOException
220     *             if user's single sign on token is invalid.
221     */
222    public IdSearchResults searchIdentities(IdType type, String pattern,
223            Map avPairs, boolean recursive, int maxResults, int maxTime,
224            Set returnAttributes, boolean returnAllAttributes)
225            throws IdRepoException, SSOException {
226        IdSearchControl crtl = new IdSearchControl();
227        crtl.setSearchModifiers(IdSearchOpModifier.OR, avPairs);
228        crtl.setRecursive(recursive);
229        crtl.setMaxResults(maxResults);
230        crtl.setTimeOut(maxTime);
231        crtl.setReturnAttributes(returnAttributes);
232        crtl.setAllReturnAttributes(returnAllAttributes);
233        
234        // Call search method that takes IdSearchControl
235        return searchIdentities(type, pattern, crtl);
236    }
237
238    /**
239     * @supported.api
240     * 
241     * Searches for identities of certain types from each plugin and returns a
242     * combined result
243     * 
244     * <b>Note:</b> The AMIdentity objects representing IdType.REALM can be
245     * used for services related operations only. The realm <code>AMIdentity
246     * </code> object can be used to assign and unassign services containing
247     * dynamic attributes to this realm.
248     *
249     * @param type
250     *            Type of identity being searched for.
251     * @param pattern
252     *            Pattern to be used when searching.
253     * @param ctrl
254     *            IdSearchControl which can be used to set up various search
255     *            controls on the search to be performed.
256     * @return Returns the combined results in an object IdSearchResults.
257     * @see com.sun.identity.idm.IdSearchControl
258     * @see com.sun.identity.idm.IdSearchResults
259     * @throws IdRepoException
260     *             if there are repository related error conditions.
261     * @throws SSOException
262     *             if user's single sign on token is invalid.
263     */
264    public IdSearchResults searchIdentities(IdType type, String pattern,
265            IdSearchControl ctrl) throws IdRepoException, SSOException {
266        IdSearchResults idSearchResults = null;
267
268        if (type.equals(IdType.REALM)) {
269            try {
270                idSearchResults = new IdSearchResults(type, idRealmName);
271                OrganizationConfigManager orgMgr =
272                    new OrganizationConfigManager(token, idRealmName);
273                Set realmNames = orgMgr.getSubOrganizationNames(pattern, false);
274                if (realmNames != null) {
275                    Iterator iter = realmNames.iterator();
276                    while (iter.hasNext()) {
277                        String realmName = (String) iter.next();
278
279                        AMIdentity realmIdentity = getSubRealmIdentity(
280                                realmName);
281                        Map attributes = new HashMap();
282                        // TODO: To add attribute support to realms.
283                        // Un comment this part once the support is added.
284                        idSearchResults.addResult(realmIdentity, attributes);
285                        idSearchResults.setErrorCode(IdSearchResults.SUCCESS);
286                    }
287                }
288            } catch (SMSException sme) {
289                debug.error("AMIdentityRepository.searchIdentities() - "
290                        + "Error occurred while searching " + type.getName()
291                        + ":", sme);
292                throw new IdRepoException(sme.getMessage());
293            }
294        } else {
295            IdServices idServices =
296                IdServicesFactory.getDataStoreServices();
297
298            idSearchResults = idServices.search(token, type, pattern, ctrl,
299                    organizationDN);
300        }
301
302        return idSearchResults;
303
304    }
305
306    /**
307     * @supported.api
308     * 
309     * Returns a handle of the Identity object representing this
310     * realm for services related operations only. This <code> AMIdentity
311     * </code> object can be used to assign and unassign services containing
312     * dynamic attributes to this realm
313     * 
314     * @return a handle of the Identity object.
315     * @throws IdRepoException
316     *             if there are repository related error conditions.
317     * @throws SSOException
318     *             if user's single sign on token is invalid.
319     */
320    public AMIdentity getRealmIdentity() throws IdRepoException, SSOException {
321        return getRealmIdentity(token, organizationDN);
322    }
323
324    private AMIdentity getRealmIdentity(SSOToken token, String orgDN)
325        throws IdRepoException {
326        String universalId = "id=ContainerDefaultTemplateRole,ou=realm," +
327            orgDN;
328        return IdUtils.getIdentity(token, universalId);
329    }
330
331    private AMIdentity getSubRealmIdentity(String subRealmName) throws
332        IdRepoException, SSOException {
333        String realmName = idRealmName;
334        if (DN.isDN(idRealmName)) {  // Wouldn't be a DN if it starts with "/"
335            realmName = DNMapper.orgNameToRealmName(idRealmName);
336        }
337
338        String fullRealmName = realmName + IdConstants.SLASH_SEPARATOR +
339            subRealmName;
340        String subOrganizationDN = DNMapper.orgNameToDN(fullRealmName);
341
342        return getRealmIdentity(token, subOrganizationDN);
343    }
344
345    /**
346     * @supported.api
347     *
348     * Creates a single object of a type. The object is
349     * created in all the plugins that support creation of this type of object.
350     * 
351     * This method is only valid for:
352     *
353     * <ol>
354     * <li> {@link IdType#AGENT IdType.AGENT} </li>
355     * <li> {@link IdType#USER  IdType.USER}  </li>
356     * <li> {@link IdType#REALM  IdType.REALM}  </li>
357     * </ol>
358     *
359     * <br>
360     * <b>Note:</b> For creating {@link IdType#REALM  IdType.REALM} identities,
361     * a map of <code>sunIdentityRepositoryService</code> attributes need to
362     * be passed. Also, AMIdentity object representing this realm can be
363     * used for services related operations only. This <code> AMIdentity
364     * </code> object can be used to assign and unassign services containing
365     * dynamic attributes to this realm
366     *
367     * 
368     * @param type
369     *            <code>IdType</code> of object to be created.
370     * @param idName
371     *            Name of object. If the type is <code>IdType.REALM</code>
372     *            then enter a valid realm name.
373     * @param attrMap
374     *            Map of attribute-values to be set when creating the entry.
375     * @return Identity object representing the newly created entry.
376     * @throws IdRepoException
377     *             if there are repository related error conditions.
378     * @throws SSOException
379     *             if user's single sign on token is invalid.
380     */
381    public AMIdentity createIdentity(IdType type, String idName, Map attrMap)
382            throws IdRepoException, SSOException {
383        IdServices idServices = IdServicesFactory.getDataStoreServices();
384        return idServices.create(token, type, idName, attrMap, organizationDN);
385    }
386
387    /**
388     * @supported.api
389     * 
390     * Creates multiple objects of the same type. The objects are created in all
391     * the <code>IdRepo</code> plugins that support creation of these objects.
392     * 
393     * This method is only valid for:
394     *
395     * <ol>
396     * <li> {@link IdType#AGENT IdType.AGENT} </li>
397     * <li> (@link IdType#USER  IdType.USER}  </li>
398     * <li> {@link IdType#REALM  IdType.REALM}  </li>
399     * </ol>
400     *
401     * <br>
402     * <b>Note:</b> For creating {@link IdType#REALM  IdType.REALM} identities,
403     * a map of <code>sunIdentityRepositoryService</code> attributes need to
404     * be passed. Also, AMIdentity object representing this realm can be
405     * used for services related operations only. This <code> AMIdentity
406     * </code> object can be used to assign and unassign services containing
407     * dynamic attributes to this realm.
408     * 
409     * @param type
410     *            Type of object to be created
411     * @param identityNamesAndAttrs
412     *            Names of the identities and their
413     * @return Set of created Identities.
414     * @throws IdRepoException
415     *             if there are repository related error conditions.
416     * @throws SSOException
417     *             if user's single sign on token is invalid.
418     */
419    public Set createIdentities(IdType type, Map identityNamesAndAttrs)
420            throws IdRepoException, SSOException {
421        Set results = new HashSet();
422
423        if (identityNamesAndAttrs == null || identityNamesAndAttrs.isEmpty()) {
424            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, "201", null);
425        }
426
427        Iterator it = identityNamesAndAttrs.keySet().iterator();
428
429        while (it.hasNext()) {
430            String name = (String) it.next();
431            Map attrMap = (Map) identityNamesAndAttrs.get(name);
432            AMIdentity id = createIdentity(type, name, attrMap);
433            results.add(id);
434        }
435
436        return results;
437    }
438
439    /**
440     * @supported.api
441     * 
442     * Deletes identities. The Set passed is a set of <code>AMIdentity</code>
443     * objects.
444     * 
445     * This method is only valid for:
446     * <ol>
447     * <li> {@link IdType#AGENT IdType.AGENT} </li>
448     * <li> {@link IdType#REALM IdType.REALM} </li>
449     * <li> (@link IdType#USER IdType.USER} </li>
450     * </ol>
451     * 
452     * @param type Type of Identity to be deleted.
453     * @param identities Set of <code>AMIdentity</code> objects to be deleted.
454     * @throws IdRepoException if there are repository related error conditions.
455     * @throws SSOException if user's single sign on token is invalid.
456     * @deprecated As of release AM 7.1, replaced by
457     *             {@link #deleteIdentities(Set)}
458     */
459    public void deleteIdentities(IdType type, Set identities)
460            throws IdRepoException, SSOException {
461        deleteIdentities(identities);
462    }
463
464    /**
465     * @supported.api
466     * 
467     * Deletes identities. The Set passed is a set of <code>AMIdentity</code>
468     * objects.
469     * 
470     * This method is only valid for:
471     * <ol>
472     * <li> {@link IdType#AGENT IdType.AGENT} </li>
473     * <li> {@link IdType#REALM IdType.REALM} </li>
474     * <li> (@link IdType#USER  IdType.USER}  </li>
475     * </ol>
476     * 
477     * @param identities Set of <code>AMIdentity</code> objects to be deleted
478     * @throws IdRepoException if there are repository related error conditions.
479     * @throws SSOException if user's single sign on token is invalid.
480     */
481    public void deleteIdentities(Set identities) throws IdRepoException,
482            SSOException {
483        if (identities == null || identities.isEmpty()) {
484            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, "201", null);
485        }
486
487        Iterator it = identities.iterator();
488        while (it.hasNext()) {
489            AMIdentity id = (AMIdentity) it.next();
490            IdServices idServices = IdServicesFactory.getDataStoreServices();
491            idServices.delete(token, id.getType(), id.getName(), organizationDN, 
492                    id.getDN());
493        }
494    }
495
496    /**
497     * Non-javadoc, non-public methods Returns <code>true</code> if the data
498     * store has successfully authenticated the identity with the provided
499     * credentials. In case the data store requires additional credentials, the
500     * list would be returned via the <code>IdRepoException</code> exception.
501     * 
502     * @param credentials
503     *            Array of callback objects containing information such as
504     *            username and password.
505     * 
506     * @return <code>true</code> if data store authenticates the identity;
507     *         else <code>false</code>
508     */
509    public boolean authenticate(Callback[] credentials) throws IdRepoException,
510            com.sun.identity.authentication.spi.AuthLoginException {
511        IdServices idServices = IdServicesFactory.getDataStoreServices();
512        return (idServices.authenticate(organizationDN, credentials));
513    }
514
515    /**
516     * @supported.api
517     * 
518     * Adds a listener, which should receive notifications for all changes that
519     * occurred in this organization.
520     * 
521     * This method is only valid for IdType User and Agent.
522     * 
523     * @param listener
524     *            The callback which implements <code>AMEventListener</code>.
525     * @return Integer identifier for this listener.
526     */
527    public int addEventListener(IdEventListener listener) {
528        ArrayList listOfListeners = (ArrayList) listeners.get(organizationDN);
529        if (listOfListeners == null) {
530            listOfListeners = new ArrayList();
531        }
532        synchronized (listeners) {
533            listOfListeners.add(listener);
534            listeners.put(organizationDN, listOfListeners);
535        }
536        return (listOfListeners.size() - 1);
537    }
538
539    /**
540     * @supported.api
541     * 
542     * Removes listener as the application is no longer interested in receiving
543     * notifications.
544     * 
545     * @param identifier
546     *            Integer identifying the listener.
547     */
548    public void removeEventListener(int identifier) {
549        ArrayList listOfListeners = (ArrayList) listeners.get(organizationDN);
550        if (listOfListeners != null) {
551            synchronized (listeners) {
552                listOfListeners.remove(identifier);
553            }
554        }
555    }
556
557    /**
558     * @supported.api
559     * 
560     * Clears the cache.
561     */
562    public static void clearCache() {
563        IdServices idServices = IdServicesFactory.getDataStoreServices();
564        idServices.reinitialize();
565        IdUtils.initialize();
566    }
567
568
569    public IdSearchResults getSpecialIdentities(SSOToken token, IdType type,
570            String orgName) throws IdRepoException, SSOException {
571        IdServices idServices = IdServicesFactory.getDataStoreServices();
572        return idServices.getSpecialIdentities(token, type, orgName);
573    }
574    
575    /**
576     * Return String representation of the <code>AMIdentityRepository
577     * </code> object. It returns realm name.
578     *
579     * @return String representation of <code>AMIdentityRepository</code>
580     * object.
581     */
582    public String toString() {
583        StringBuilder sb = new StringBuilder(100);
584        sb.append("AMIdentityRepository object: ")
585            .append(organizationDN);
586        return (sb.toString());
587    }
588
589    // TODO:
590    // FIXME: Move these utilities to a util class
591    private Map reverseMapAttributeNames(Map attrMap, Map configMap) {
592        if (attrMap == null || attrMap.isEmpty()) {
593            return attrMap;
594        }
595        Map resultMap;
596        Map[] mapArray = getAttributeNameMap(configMap);
597        if (mapArray == null) {
598            resultMap = attrMap;
599        } else {
600            resultMap = new CaseInsensitiveHashMap();
601            Map reverseMap = mapArray[1];
602            Iterator it = attrMap.keySet().iterator();
603            while (it.hasNext()) {
604                String curr = (String) it.next();
605                if (reverseMap.containsKey(curr)) {
606                    resultMap.put((String) reverseMap.get(curr), (Set) attrMap
607                            .get(curr));
608                } else {
609                    resultMap.put(curr, (Set) attrMap.get(curr));
610                }
611            }
612        }
613        return resultMap;
614    }
615
616    private IdSearchResults combineSearchResults(SSOToken token,
617            Object[][] arrayOfResult, int sizeOfArray, IdType type,
618            String orgName, boolean amsdkIncluded, Object[][] amsdkResults) {
619        Map amsdkDNs = new CaseInsensitiveHashMap();
620        Map resultsMap = new CaseInsensitiveHashMap();
621        int errorCode = IdSearchResults.SUCCESS;
622        if (amsdkIncluded) {
623            RepoSearchResults amsdkRepoRes = (RepoSearchResults) 
624                amsdkResults[0][0];
625            Set results = amsdkRepoRes.getSearchResults();
626            Map attrResults = amsdkRepoRes.getResultAttributes();
627            Iterator it = results.iterator();
628            while (it.hasNext()) {
629                String dn = (String) it.next();
630                String name = LDAPDN.explodeDN(dn, true)[0];
631                amsdkDNs.put(name, dn);
632                Set attrMaps = new HashSet();
633                attrMaps.add((Map) attrResults.get(dn));
634                resultsMap.put(name, attrMaps);
635            }
636            errorCode = amsdkRepoRes.getErrorCode();
637        }
638        for (int i = 0; i < sizeOfArray; i++) {
639            RepoSearchResults current = (RepoSearchResults) arrayOfResult[i][0];
640            Map configMap = (Map) arrayOfResult[i][1];
641            Iterator it = current.getSearchResults().iterator();
642            Map allAttrMaps = current.getResultAttributes();
643            while (it.hasNext()) {
644                String m = (String) it.next();
645                String mname = DNUtils.DNtoName(m);
646                Map attrMap = (Map) allAttrMaps.get(m);
647                attrMap = reverseMapAttributeNames(attrMap, configMap);
648                Set attrMaps = (Set) resultsMap.get(mname);
649                if (attrMaps == null) {
650                    attrMaps = new HashSet();
651                }
652                attrMaps.add(attrMap);
653                resultsMap.put(mname, attrMaps);
654            }
655        }
656        IdSearchResults results = new IdSearchResults(type, orgName);
657        Iterator it = resultsMap.keySet().iterator();
658        while (it.hasNext()) {
659            String mname = (String) it.next();
660            Map combinedMap = combineAttrMaps((Set) resultsMap.get(mname), 
661                    true);
662            AMIdentity id = new AMIdentity(token, mname, type, orgName,
663                    (String) amsdkDNs.get(mname));
664            results.addResult(id, combinedMap);
665        }
666        results.setErrorCode(errorCode);
667        return results;
668    }
669
670    private Map[] getAttributeNameMap(Map configMap) {
671        Set attributeMap = (Set) configMap.get(IdConstants.ATTR_MAP);
672
673        if (attributeMap == null || attributeMap.isEmpty()) {
674            return null;
675        } else {
676            Map returnArray[] = new Map[2];
677            int size = attributeMap.size();
678            returnArray[0] = new CaseInsensitiveHashMap(size);
679            returnArray[1] = new CaseInsensitiveHashMap(size);
680            Iterator it = attributeMap.iterator();
681            while (it.hasNext()) {
682                String mapString = (String) it.next();
683                int eqIndex = mapString.indexOf('=');
684                if (eqIndex > -1) {
685                    String first = mapString.substring(0, eqIndex);
686                    String second = mapString.substring(eqIndex + 1);
687                    returnArray[0].put(first, second);
688                    returnArray[1].put(second, first);
689                } else {
690                    returnArray[0].put(mapString, mapString);
691                    returnArray[1].put(mapString, mapString);
692                }
693            }
694            return returnArray;
695        }
696    }
697
698    private Map combineAttrMaps(Set setOfMaps, boolean isString) {
699        // Map resultMap = new CaseInsensitiveHashMap();
700        Map resultMap = new AMHashMap();
701        Iterator it = setOfMaps.iterator();
702        while (it.hasNext()) {
703            Map currMap = (Map) it.next();
704            if (currMap != null) {
705                Iterator keyset = currMap.keySet().iterator();
706                while (keyset.hasNext()) {
707                    String thisAttr = (String) keyset.next();
708                    if (isString) {
709                        Set resultSet = (Set) resultMap.get(thisAttr);
710                        Set thisSet = (Set) currMap.get(thisAttr);
711                        if (resultSet != null) {
712                            resultSet.addAll(thisSet);
713                        } else {
714                            /*
715                             * create a new Set so that we do not alter the set
716                             * that is referenced in setOfMaps
717                             */
718                            resultSet = new HashSet((Set) 
719                                    currMap.get(thisAttr));
720                            resultMap.put(thisAttr, resultSet);
721                        }
722                    } else { // binary attributes
723
724                        byte[][] resultSet = (byte[][]) resultMap.get(thisAttr);
725                        byte[][] thisSet = (byte[][]) currMap.get(thisAttr);
726                        int combinedSize = thisSet.length;
727                        if (resultSet != null) {
728                            combinedSize = resultSet.length + thisSet.length;
729                            byte[][] tmpSet = new byte[combinedSize][];
730                            for (int i = 0; i < resultSet.length; i++) {
731                                tmpSet[i] = (byte[]) resultSet[i];
732                            }
733                            for (int i = 0; i < thisSet.length; i++) {
734                                tmpSet[i] = (byte[]) thisSet[i];
735                            }
736                            resultSet = tmpSet;
737                        } else {
738                            resultSet = (byte[][]) thisSet.clone();
739                        }
740                        resultMap.put(thisAttr, resultSet);
741
742                    }
743
744                }
745            }
746        }
747        return resultMap;
748    }
749}




























































Copyright © 2010-2017, ForgeRock All Rights Reserved.