1 /*
2 * ====================
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 *
5 * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
6 *
7 * The contents of this file are subject to the terms of the Common Development
8 * and Distribution License("CDDL") (the "License"). You may not use this file
9 * except in compliance with the License.
10 *
11 * You can obtain a copy of the License at
12 * http://IdentityConnectors.dev.java.net/legal/license.txt
13 * See the License for the specific language governing permissions and limitations
14 * under the License.
15 *
16 * When distributing the Covered Code, include this CDDL Header Notice in each file
17 * and include the License file at identityconnectors/legal/license.txt.
18 * If applicable, add the following below this CDDL Header, with the fields
19 * enclosed by brackets [] replaced by your own identifying information:
20 * "Portions Copyrighted [year] [name of copyright owner]"
21 * ====================
22 */
23 package org.identityconnectors.framework.common.objects;
24
25 import static org.identityconnectors.framework.common.objects.NameUtil.nameHashCode;
26 import static org.identityconnectors.framework.common.objects.NameUtil.namesEqual;
27
28 import java.util.Collections;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32
33 import org.identityconnectors.common.CollectionUtil;
34 import org.identityconnectors.common.StringUtil;
35 import org.identityconnectors.common.security.GuardedString;
36
37
38 /**
39 * Represents a named collection of values within a target object, although
40 * the simplest case is a name-value pair (e.g., email, employeeID). Values can
41 * be empty, null, or set with various types. Empty and null are supported
42 * because it makes a difference on some resources (in particular database
43 * resources).
44 * <p>
45 * The developer of a Connector should use an {@link AttributeBuilder} to
46 * construct an instance of Attribute.
47 * <p>
48 * The precise meaning of an instance of {@code Attribute} depends
49 * on the context in which it occurs.
50 * <ul>
51 * <li>When
52 * {@linkplain org.identityconnectors.framework.api.operations.GetApiOp#getObject
53 * an object is read} or is returned by
54 * {@linkplain org.identityconnectors.framework.api.operations.SearchApiOp#search search},
55 * an {@code Attribute} represents the <i>complete state</i> of an attribute
56 * of the target object, current as of the point in time that the object was read.
57 * </li>
58 * <li>When an {@code Attribute} is supplied to
59 * {@linkplain org.identityconnectors.framework.api.operations.UpdateApiOp
60 * the update operation},
61 * the {@code Attribute} represents a change
62 * to the corresponding attribute of the target object:
63 * <ul>
64 * <li>For calls to
65 * {@link org.identityconnectors.framework.api.operations.UpdateApiOp#update(ObjectClass, Uid, java.util.Set, OperationOptions) update},
66 * the {@code Attribute} contains the <i>complete, intended state</i> of the attribute.
67 * </li>
68 * <li>When the update type is
69 * {@link org.identityconnectors.framework.api.operations.UpdateApiOp#addAttributeValues(ObjectClass, Uid, java.util.Set, OperationOptions) addAttributeValues},
70 * the {@code Attribute} contains <i>values to append</i>.
71 * </li>
72 * <li>When the update type is
73 * {@link org.identityconnectors.framework.api.operations.UpdateApiOp#removeAttributeValues(ObjectClass, Uid, java.util.Set, OperationOptions) removeAttributeValues},
74 * the {@code Attribute} contains <i>values to remove</i>.
75 * </li>
76 * </ul>
77 * </li>
78 * <li>When an {@code Attribute} is used to build a
79 * {@link org.identityconnectors.framework.common.objects.filter.Filter Filter}
80 * that is an argument to
81 * {@linkplain org.identityconnectors.framework.api.operations.SearchApiOp#search search},
82 * an {@code Attribute} represents a <i>subset of the current state</i> of an attribute
83 * that will be used as a search criterion.
84 * Specifically, the {@code Attribute} {@linkplain #getName() names the attribute to match}
85 * and {@linkplain #getValue() contains the values to match}.
86 * </li>
87 * </ul>
88 *
89 * TODO: define the set of allowed values
90 *
91 * @author Will Droste
92 * @version $Revision: 1.7 $
93 * @since 1.0
94 */
95 public class Attribute {
96 /**
97 * Name of the {@link Attribute}.
98 */
99 private final String name;
100
101 /**
102 * Values of the {@link Attribute}.
103 */
104 private final List<Object> value;
105
106 /**
107 * Create an attribute.
108 */
109 Attribute(String name, List<Object> value) {
110 if (StringUtil.isBlank(name)) {
111 throw new IllegalArgumentException("Name must not be blank!");
112 }
113 if (OperationalAttributes.PASSWORD_NAME.equals(name)
114 || OperationalAttributes.CURRENT_PASSWORD_NAME.equals(name)) {
115 // check the value..
116 if (value == null || value.size() != 1) {
117 final String MSG = "Must be a single value.";
118 throw new IllegalArgumentException(MSG);
119 }
120 if (!(value.get(0) instanceof GuardedString)) {
121 final String MSG = "Password value must be an instance of GuardedString";
122 throw new IllegalArgumentException(MSG);
123 }
124 }
125 // make this case insensitive
126 this.name = name;
127 // copy to prevent corruption..
128 this.value = (value == null) ? null : CollectionUtil.newReadOnlyList(value);
129 }
130
131 public String getName() {
132 return this.name;
133 }
134
135 public List<Object> getValue() {
136 return (this.value == null) ? null : Collections
137 .unmodifiableList(this.value);
138 }
139
140 /**
141 * Determines if the 'name' matches this {@link Attribute}.
142 *
143 * @param name
144 * case insensitive string representation of the attribute's
145 * name.
146 * @return <code>true</code> iff the case insentitive name is equal to
147 * that of the one in {@link Attribute}.
148 */
149 public boolean is(String name) {
150 return namesEqual(this.name, name);
151 }
152
153 // ===================================================================
154 // Object Overrides
155 // ===================================================================
156 @Override
157 public final int hashCode() {
158 return nameHashCode(name);
159 }
160
161 @Override
162 public String toString() {
163 // poor man's consistent toString impl..
164 StringBuilder bld = new StringBuilder();
165 bld.append("Attribute: ");
166 Map<String, Object> map = new LinkedHashMap<String, Object>();
167 map.put("Name", getName());
168 map.put("Value", getValue());
169 bld.append(map);
170 return bld.toString();
171 }
172
173 @Override
174 public final boolean equals(Object obj) {
175 // test identity
176 if (this == obj) {
177 return true;
178 }
179 // test for null..
180 if (obj == null) {
181 return false;
182 }
183 // test that the exact class matches
184 if (!(getClass().equals(obj.getClass()))) {
185 return false;
186 }
187 // test name field..
188 final Attribute other = (Attribute) obj;
189 if (!is(other.name)) {
190 return false;
191 }
192
193 if (!CollectionUtil.equals(value, other.value)) {
194 return false;
195 }
196 return true;
197 }
198 }