001 /*
002 * Copyright 2011-2012 UnboundID Corp.
003 *
004 * This program is free software; you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License (GPLv2 only)
006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
007 * as published by the Free Software Foundation.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program; if not, see <http://www.gnu.org/licenses>.
016 */
017
018 package com.unboundid.scim.schema;
019
020 import com.unboundid.scim.data.AttributeValueResolver;
021 import com.unboundid.scim.data.BaseResource;
022 import com.unboundid.scim.data.ResourceFactory;
023 import com.unboundid.scim.sdk.InvalidResourceException;
024 import com.unboundid.scim.sdk.SCIMConstants;
025 import com.unboundid.scim.sdk.SCIMObject;
026
027 import java.util.Collection;
028 import java.util.HashMap;
029 import java.util.Map;
030 import java.util.Set;
031
032 import static com.unboundid.scim.sdk.StaticUtils.toLowerCase;
033
034
035
036 /**
037 * This class provides methods that describe the schema for a SCIM resource. It
038 * may be used to help read and write SCIM objects in their external XML and
039 * JSON representation, and to convert SCIM objects to and from LDAP entries.
040 */
041 public class ResourceDescriptor extends BaseResource
042 {
043 /**
044 * A <code>ResourceFactory</code> for creating <code>ResourceDescriptor</code>
045 * instances.
046 */
047 public static final ResourceFactory<ResourceDescriptor>
048 RESOURCE_DESCRIPTOR_FACTORY = new ResourceFactory<ResourceDescriptor>() {
049 /**
050 * {@inheritDoc}
051 */
052 public ResourceDescriptor createResource(
053 final ResourceDescriptor resourceDescriptor,
054 final SCIMObject scimObject) {
055 return new ResourceDescriptor(resourceDescriptor, scimObject);
056 }
057 };
058
059 /**
060 * A schema -> name -> AttributeDescriptor map to quickly look up
061 * attributes. The attribute descriptors are keyed by the lower case
062 * attribute name because attribute names are case-insensitive. Likewise,
063 * the schema key is lower case because schema URNs are case-insensitive.
064 */
065 private Map<String, Map<String, AttributeDescriptor>> attributesCache;
066
067 /**
068 * Constructs a new ResourceDescriptor from a existing SCIMObject.
069 *
070 * @param resourceDescriptor The Resource Schema descriptor.
071 * @param scimObject The SCIMObject containing the schema.
072 */
073 ResourceDescriptor(final ResourceDescriptor resourceDescriptor,
074 final SCIMObject scimObject) {
075 super(resourceDescriptor, scimObject);
076 }
077
078 /**
079 * Constructs a new empty ResourceDescriptor.
080 *
081 * @param resourceDescriptor The Resource Schema descriptor.
082 */
083 private ResourceDescriptor(final ResourceDescriptor resourceDescriptor) {
084 super(resourceDescriptor);
085 }
086
087 /**
088 * Retrieves the attribute descriptor for a specified attribute.
089 *
090 * @param schema The attribute descriptor's associated schema URN.
091 * @param name The name of the attribute whose descriptor is to be retrieved.
092 *
093 * @return The attribute descriptor for the specified attribute.
094 * @throws InvalidResourceException if there is no such attribute.
095 */
096 public AttributeDescriptor getAttribute(final String schema,
097 final String name)
098 throws InvalidResourceException
099 {
100 // TODO: Should we implement a strict and non strict mode?
101 initAttributesCache();
102 AttributeDescriptor attributeDescriptor = null;
103 Map<String, AttributeDescriptor> map =
104 attributesCache.get(toLowerCase(schema));
105 if(map != null)
106 {
107 attributeDescriptor = map.get(toLowerCase(name));
108 }
109 if(attributeDescriptor == null)
110 {
111 throw new InvalidResourceException("Attribute " + schema +
112 SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE + name +
113 " is not defined for resource " + getName());
114 }
115 return attributeDescriptor;
116 }
117
118 /**
119 * Retrieves all the attribute descriptors of the provided schema defined
120 * in the resource.
121 *
122 * @param schema The name of the schema.
123 * @return All the attribute descriptors of the provided schema defined
124 * for this resource.
125 */
126 public Collection<AttributeDescriptor> getAttributes(final String schema)
127 {
128 initAttributesCache();
129 Map<String, AttributeDescriptor> map =
130 attributesCache.get(toLowerCase(schema));
131 if(map != null)
132 {
133 return map.values();
134 }
135 return null;
136 }
137
138 /**
139 * Retrieves the set of unique schemas for the attribute descriptors defined
140 * in the resource.
141 *
142 * @return The set of unique schemas for the attribute descriptors defined
143 * in the resource.
144 */
145 public Set<String> getAttributeSchemas()
146 {
147 initAttributesCache();
148 return attributesCache.keySet();
149 }
150
151 /**
152 * Retrieve the name of the resource to be used in any external representation
153 * of the resource.
154 *
155 * @return Retrieve the name of the resource to be used in any external
156 * representation of the resource. It is never {@code null}.
157 */
158 public String getName()
159 {
160 return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "name",
161 AttributeValueResolver.STRING_RESOLVER);
162 }
163
164
165
166 /**
167 * Sets the name of the resource to be used in any external representation
168 * of the resource.
169 *
170 * @param name The name of the resource to be used in any external
171 * representation of the resource.
172 * @return this ResourceDescriptor.
173 */
174 private ResourceDescriptor setName(final String name)
175 {
176 try {
177 setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
178 "name", AttributeValueResolver.STRING_RESOLVER, name);
179 } catch (InvalidResourceException e) {
180 // This should never happen as these are core attributes...
181 throw new RuntimeException(e);
182 }
183 return this;
184 }
185
186
187
188 /**
189 * Retrieves the list of all attribute descriptors defined in the resource.
190 *
191 * @return The list of attribute descriptors for the resource. It is never
192 * {@code null}.
193 */
194 public Collection<AttributeDescriptor> getAttributes()
195 {
196 return getAttributeValues(SCIMConstants.SCHEMA_URI_CORE,
197 "attributes", AttributeDescriptor.ATTRIBUTE_DESCRIPTOR_RESOLVER);
198 }
199
200
201
202 /**
203 * Sets the list of attribute descriptors for the resource.
204 *
205 * @param attributes The list of attribute descriptors for the resource.
206 * @return this ResourceDescriptor.
207 */
208 private ResourceDescriptor setAttributes(
209 final Collection<AttributeDescriptor> attributes)
210 {
211 try {
212 setAttributeValues(SCIMConstants.SCHEMA_URI_CORE,
213 "attributes", AttributeDescriptor.ATTRIBUTE_DESCRIPTOR_RESOLVER,
214 attributes);
215 } catch (InvalidResourceException e) {
216 // This should never happen as these are core attributes...
217 throw new RuntimeException(e);
218 }
219 return this;
220 }
221
222
223
224 /**
225 * Returns the resource's XML schema (namespace) name.
226 *
227 * @return The XML namespace name.
228 */
229 public String getSchema()
230 {
231 return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "schema",
232 AttributeValueResolver.STRING_RESOLVER);
233 }
234
235 /**
236 * Sets the resource's XML schema (namespace) name.
237 *
238 * @param schema The XML namespace name.
239 * @return this ResourceDescriptor.
240 */
241 private ResourceDescriptor setSchema(final String schema)
242 {
243 try {
244 setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
245 "schema", AttributeValueResolver.STRING_RESOLVER, schema);
246 } catch (InvalidResourceException e) {
247 // This should never happen as these are core attributes...
248 throw new RuntimeException(e);
249 }
250 return this;
251 }
252
253 /**
254 * Retrieves the resource's human readable description.
255 *
256 * @return The resource's human readable description.
257 */
258 public String getDescription()
259 {
260 return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
261 "description", AttributeValueResolver.STRING_RESOLVER);
262 }
263
264 /**
265 * Sets the resource's human readable description.
266 *
267 * @param description The resource's human readable description.
268 * @return this ResourceDescriptor.
269 */
270 private ResourceDescriptor setDescription(final String description)
271 {
272 try {
273 setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
274 "description", AttributeValueResolver.STRING_RESOLVER, description);
275 } catch (InvalidResourceException e) {
276 // This should never happen as these are core attributes...
277 throw new RuntimeException(e);
278 }
279 return this;
280 }
281
282 /**
283 * Retrieves the Resource's HTTP addressable endpoint relative to the
284 * Base URL.
285 *
286 * @return The Resource's HTTP addressable endpoint relative to the Base URL.
287 */
288 public String getEndpoint()
289 {
290 return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
291 "endpoint", AttributeValueResolver.STRING_RESOLVER);
292 }
293
294
295 /**
296 * Sets the Resource's HTTP addressable endpoint relative to the
297 * Base URL.
298 *
299 * @param endpoint The Resource's HTTP addressable endpoint relative to
300 * the Base URL.
301 * @return this ResourceDescriptor.
302 */
303 private ResourceDescriptor setEndpoint(final String endpoint)
304 {
305 try {
306 setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
307 "endpoint", AttributeValueResolver.STRING_RESOLVER, endpoint);
308 } catch (InvalidResourceException e) {
309 // This should never happen as these are core attributes...
310 throw new RuntimeException(e);
311 }
312 return this;
313 }
314
315 /**
316 * Initializes the attributesCache if needed.
317 */
318 private void initAttributesCache()
319 {
320 synchronized(this)
321 {
322 if(attributesCache == null)
323 {
324 attributesCache = new HashMap<String,
325 Map<String, AttributeDescriptor>>();
326 for(AttributeDescriptor attributeDescriptor : getAttributes())
327 {
328 final String lowerCaseSchema =
329 toLowerCase(attributeDescriptor.getSchema());
330 Map<String, AttributeDescriptor> map =
331 attributesCache.get(lowerCaseSchema);
332 if(map == null)
333 {
334 map = new HashMap<String, AttributeDescriptor>();
335 attributesCache.put(lowerCaseSchema, map);
336 }
337 map.put(toLowerCase(attributeDescriptor.getName()),
338 attributeDescriptor);
339 }
340 }
341 }
342 }
343
344 /**
345 * {@inheritDoc}
346 */
347 @Override
348 public int hashCode()
349 {
350 int hashCode = 0;
351
352 hashCode += toLowerCase(getSchema()).hashCode();
353 hashCode += toLowerCase(getName()).hashCode();
354
355 return hashCode;
356 }
357
358 /**
359 * {@inheritDoc}
360 */
361 @Override
362 public boolean equals(final Object obj)
363 {
364 if (this == obj)
365 {
366 return true;
367 }
368
369 if (!(obj instanceof ResourceDescriptor))
370 {
371 return false;
372 }
373
374 final ResourceDescriptor that = (ResourceDescriptor)obj;
375 final String thisSchema = getSchema();
376 final String thisName = getName();
377 final String thatSchema = that.getSchema();
378 final String thatName = that.getName();
379 if (thisSchema == null && thatSchema == null)
380 {
381 return thisName.equalsIgnoreCase(thatName);
382 }
383 else
384 {
385 return thisSchema != null && thatSchema != null &&
386 thisSchema.equalsIgnoreCase(thatSchema) &&
387 thisName.equalsIgnoreCase(thatName);
388 }
389 }
390
391 @Override
392 public String toString()
393 {
394 return "ResourceDescriptor{" +
395 "name='" + getName() + '\'' +
396 ", description='" + getDescription() +
397 ", schema='" + getSchema() + '\'' +
398 ", endpoint='" + getEndpoint() + '\'' +
399 ", attributes=" + getAttributes() +
400 '}';
401 }
402
403 /**
404 * Construct a new resource descriptor with the provided information.
405 * The resource attributes specified here should not include common core
406 * attributes (ie. id, externalId, meta) as these will be added automatically.
407 *
408 * @param name The addressable Resource endpoint name.
409 * @param description The Resource's human readable description.
410 * @param schema The Resource's associated schema URN
411 * @param endpoint The Resource's HTTP addressable endpoint relative
412 * to the Base URL.
413 * @param attributes Specifies the set of associated Resource attributes.
414 * @return The newly constructed resource descriptor.
415 */
416 public static ResourceDescriptor create(
417 final String name, final String description, final String schema,
418 final String endpoint, final AttributeDescriptor... attributes)
419 {
420 ResourceDescriptor resourceDescriptor =
421 new ResourceDescriptor(CoreSchema.RESOURCE_SCHEMA_DESCRIPTOR);
422 resourceDescriptor.setName(name);
423 resourceDescriptor.setDescription(description);
424 resourceDescriptor.setSchema(schema);
425 resourceDescriptor.setEndpoint(endpoint);
426 resourceDescriptor.setAttributes(
427 CoreSchema.addCommonResourceAttributes(attributes));
428
429 return resourceDescriptor;
430 }
431 }