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 2010-2011 ApexIdentity Inc. 015 * Portions Copyright 2011-2015 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.resolver; 019 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Collections; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026 027import org.forgerock.http.util.Loader; 028 029/** 030 * Performs object resolution by object type. A given object may have more than 031 * one resolver, depending on what class it extends and/or interfaces it 032 * implements, or what its superclasses and interfaces are. 033 */ 034public final class Resolvers { 035 036 /** 037 * Resolver that handles native arrays (not handled like the service-based 038 * resolvers). 039 */ 040 private static final List<Resolver> ARRAY_RESOLVER = Collections.unmodifiableList(Arrays 041 .asList((Resolver) new ArrayResolver())); 042 043 /** Mapping of supported classes to associated resolvers. */ 044 @SuppressWarnings("rawtypes") 045 public static final Map<Class, Resolver> SERVICES = Collections.unmodifiableMap(Loader.loadMap( 046 Class.class, Resolver.class)); 047 048 /** Static methods only. */ 049 private Resolvers() { 050 } 051 052 /** 053 * Provides an iterable object over the resolvers that are appropriate for a 054 * particular object. Resolvers are provided ordered from most specific to 055 * class/interface to least. Resolvers are provided through an iterator 056 * interface to avoid the overhead of determining all resolvers in advance. 057 * 058 * @param object the object for which a set of resolvers is being sought. 059 * @return an object that returns an iterator over the set of resolvers for 060 * the object. 061 */ 062 public static Iterable<Resolver> resolvers(final Object object) { 063 return new Iterable<Resolver>() { 064 public Iterator<Resolver> iterator() { 065 return (object.getClass().isArray() ? ARRAY_RESOLVER.iterator() : new Iterator<Resolver>() { 066 Class<?> class1 = object.getClass(); 067 Class<?> class2 = class1; 068 Iterator<Class<?>> interfaces = null; 069 int n = 0; 070 071 public boolean hasNext() { 072 // interface hierarchy not yet exhausted 073 return (class2 != null); 074 } 075 076 public Resolver next() { 077 while (class1 != null) { 078 // class hierarchy 079 Resolver resolver = SERVICES.get(class1); 080 class1 = class1.getSuperclass(); 081 if (resolver != null) { 082 return resolver; 083 } 084 } 085 // exhausted class hierarchy 086 class1 = null; 087 while (class2 != null && class2 != Object.class) { 088 // interface hierarchy 089 if (interfaces != null && interfaces.hasNext()) { 090 Resolver resolver = SERVICES.get(interfaces.next()); 091 if (resolver != null) { 092 return resolver; 093 } 094 } else { 095 List<Class<?>> list = getInterfaces(class2, n++); 096 if (list.size() > 0) { 097 interfaces = list.iterator(); 098 } else { 099 class2 = class2.getSuperclass(); 100 n = 0; 101 } 102 } 103 } 104 // exhausted interface hierarchy 105 class2 = null; 106 return new Unresolver(); 107 } 108 109 public void remove() { 110 throw new UnsupportedOperationException(); 111 } 112 }); 113 } 114 }; 115 } 116 117 /** 118 * Attempts to resolve an element of an object. 119 * 120 * @param object the object in which to resolve the specified element. 121 * @param element the element to resolve within the specified object. 122 * @return the value of the resolved element, or {@link Resolver#UNRESOLVED 123 * UNRESOLVED} if it cannot be resolved. 124 * @see Resolver#get(Object, Object) 125 */ 126 public static Object get(Object object, Object element) { 127 for (Resolver resolver : resolvers(object)) { 128 Object value = resolver.get(object, element); 129 if (value != Resolver.UNRESOLVED) { 130 // first hit wins 131 return value; 132 } 133 } 134 return Resolver.UNRESOLVED; 135 } 136 137 /** 138 * Attempts to set the value of an element of an object. 139 * 140 * @param object the object in which to resolve the specified element. 141 * @param element the element within the specified object whose value is to be 142 * set. 143 * @param value the value to set the element to. 144 * @return the previous value of the element, {@code null} if no previous 145 * value, or {@link Resolver#UNRESOLVED UNRESOLVED} if it cannot be 146 * resolved. 147 * @see Resolver#put(Object, Object, Object) 148 */ 149 public static Object put(Object object, Object element, Object value) { 150 for (Resolver resolver : resolvers(object)) { 151 Object resolved = resolver.put(object, element, value); 152 if (resolved != Resolver.UNRESOLVED) { 153 // first hit wins 154 return resolved; 155 } 156 } 157 return Resolver.UNRESOLVED; 158 } 159 160 private static List<Class<?>> getInterfaces(Class<?> c, int level) { 161 List<Class<?>> interfaces; 162 if (level == 0) { 163 interfaces = Arrays.asList(c.getInterfaces()); 164 } else { 165 interfaces = new ArrayList<>(); 166 for (Class<?> iface : c.getInterfaces()) { 167 // recursion 168 interfaces.addAll(getInterfaces(iface, level - 1)); 169 } 170 } 171 return interfaces; 172 } 173}