View Javadoc

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 }