1/*
2 * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.management.relation;
27
28import com.sun.jmx.mbeanserver.Util;
29import java.util.ArrayList;
30import java.util.Collection;
31import java.util.List;
32
33/**
34 * A RoleList represents a list of roles (Role objects). It is used as
35 * parameter when creating a relation, and when trying to set several roles in
36 * a relation (via 'setRoles()' method). It is returned as part of a
37 * RoleResult, to provide roles successfully retrieved.
38 *
39 * @since 1.5
40 */
41/* We cannot extend ArrayList<Role> because our legacy
42   add(Role) method would then override add(E) in ArrayList<E>,
43   and our return value is void whereas ArrayList.add(E)'s is boolean.
44   Likewise for set(int,Role).  Grrr.  We cannot use covariance
45   to override the most important methods and have them return
46   Role, either, because that would break subclasses that
47   override those methods in turn (using the original return type
48   of Object).  Finally, we cannot implement Iterable<Role>
49   so you could write
50       for (Role r : roleList)
51   because ArrayList<> implements Iterable<> and the same class cannot
52   implement two versions of a generic interface.  Instead we provide
53   the asList() method so you can write
54       for (Role r : roleList.asList())
55*/
56public class RoleList extends ArrayList<Object> {
57
58    private transient boolean typeSafe;
59    private transient boolean tainted;
60
61    /* Serial version */
62    private static final long serialVersionUID = 5568344346499649313L;
63
64    //
65    // Constructors
66    //
67
68    /**
69     * Constructs an empty RoleList.
70     */
71    public RoleList() {
72        super();
73    }
74
75    /**
76     * Constructs an empty RoleList with the initial capacity
77     * specified.
78     *
79     * @param initialCapacity  initial capacity
80     */
81    public RoleList(int initialCapacity) {
82        super(initialCapacity);
83    }
84
85    /**
86     * Constructs a {@code RoleList} containing the elements of the
87     * {@code List} specified, in the order in which they are returned by
88     * the {@code List}'s iterator. The {@code RoleList} instance has
89     * an initial capacity of 110% of the size of the {@code List}
90     * specified.
91     *
92     * @param list the {@code List} that defines the initial contents of
93     * the new {@code RoleList}.
94     *
95     * @exception IllegalArgumentException if the {@code list} parameter
96     * is {@code null} or if the {@code list} parameter contains any
97     * non-Role objects.
98     *
99     * @see ArrayList#ArrayList(java.util.Collection)
100     */
101    public RoleList(List<Role> list) throws IllegalArgumentException {
102        // Check for null parameter
103        //
104        if (list == null)
105            throw new IllegalArgumentException("Null parameter");
106
107        // Check for non-Role objects
108        //
109        checkTypeSafe(list);
110
111        // Build the List<Role>
112        //
113        super.addAll(list);
114    }
115
116    /**
117     * Return a view of this list as a {@code List<Role>}.
118     * Changes to the returned value are reflected by changes
119     * to the original {@code RoleList} and vice versa.
120     *
121     * @return a {@code List<Role>} whose contents
122     * reflect the contents of this {@code RoleList}.
123     *
124     * <p>If this method has ever been called on a given
125     * {@code RoleList} instance, a subsequent attempt to add
126     * an object to that instance which is not a {@code Role}
127     * will fail with an {@code IllegalArgumentException}. For compatibility
128     * reasons, a {@code RoleList} on which this method has never
129     * been called does allow objects other than {@code Role}s to
130     * be added.</p>
131     *
132     * @throws IllegalArgumentException if this {@code RoleList} contains
133     * an element that is not a {@code Role}.
134     *
135     * @since 1.6
136     */
137    @SuppressWarnings("unchecked")
138    public List<Role> asList() {
139        if (!typeSafe) {
140            if (tainted)
141                checkTypeSafe(this);
142            typeSafe = true;
143        }
144        return Util.cast(this);
145    }
146
147    //
148    // Accessors
149    //
150
151    /**
152     * Adds the Role specified as the last element of the list.
153     *
154     * @param role  the role to be added.
155     *
156     * @exception IllegalArgumentException  if the role is null.
157     */
158    public void add(Role role)
159        throws IllegalArgumentException {
160
161        if (role == null) {
162            String excMsg = "Invalid parameter";
163            throw new IllegalArgumentException(excMsg);
164        }
165        super.add(role);
166    }
167
168    /**
169     * Inserts the role specified as an element at the position specified.
170     * Elements with an index greater than or equal to the current position are
171     * shifted up.
172     *
173     * @param index  The position in the list where the new Role
174     * object is to be inserted.
175     * @param role  The Role object to be inserted.
176     *
177     * @exception IllegalArgumentException  if the role is null.
178     * @exception IndexOutOfBoundsException  if accessing with an index
179     * outside of the list.
180     */
181    public void add(int index,
182                    Role role)
183        throws IllegalArgumentException,
184               IndexOutOfBoundsException {
185
186        if (role == null) {
187            String excMsg = "Invalid parameter";
188            throw new IllegalArgumentException(excMsg);
189        }
190
191        super.add(index, role);
192    }
193
194    /**
195     * Sets the element at the position specified to be the role
196     * specified.
197     * The previous element at that position is discarded.
198     *
199     * @param index  The position specified.
200     * @param role  The value to which the role element should be set.
201     *
202     * @exception IllegalArgumentException  if the role is null.
203     * @exception IndexOutOfBoundsException  if accessing with an index
204     * outside of the list.
205     */
206     public void set(int index,
207                     Role role)
208         throws IllegalArgumentException,
209                IndexOutOfBoundsException {
210
211        if (role == null) {
212            // Revisit [cebro] Localize message
213            String excMsg = "Invalid parameter.";
214            throw new IllegalArgumentException(excMsg);
215        }
216
217        super.set(index, role);
218     }
219
220    /**
221     * Appends all the elements in the RoleList specified to the end
222     * of the list, in the order in which they are returned by the Iterator of
223     * the RoleList specified.
224     *
225     * @param roleList  Elements to be inserted into the list (can be null)
226     *
227     * @return true if this list changed as a result of the call.
228     *
229     * @exception IndexOutOfBoundsException  if accessing with an index
230     * outside of the list.
231     *
232     * @see ArrayList#addAll(Collection)
233     */
234    public boolean addAll(RoleList roleList)
235        throws IndexOutOfBoundsException {
236
237        if (roleList == null) {
238            return true;
239        }
240
241        return (super.addAll(roleList));
242    }
243
244    /**
245     * Inserts all of the elements in the RoleList specified into this
246     * list, starting at the specified position, in the order in which they are
247     * returned by the Iterator of the RoleList specified.
248     *
249     * @param index  Position at which to insert the first element from the
250     * RoleList specified.
251     * @param roleList  Elements to be inserted into the list.
252     *
253     * @return true if this list changed as a result of the call.
254     *
255     * @exception IllegalArgumentException  if the role is null.
256     * @exception IndexOutOfBoundsException  if accessing with an index
257     * outside of the list.
258     *
259     * @see ArrayList#addAll(int, Collection)
260     */
261    public boolean addAll(int index,
262                          RoleList roleList)
263        throws IllegalArgumentException,
264               IndexOutOfBoundsException {
265
266        if (roleList == null) {
267            // Revisit [cebro] Localize message
268            String excMsg = "Invalid parameter.";
269            throw new IllegalArgumentException(excMsg);
270        }
271
272        return (super.addAll(index, roleList));
273    }
274
275    /*
276     * Override all of the methods from ArrayList<Object> that might add
277     * a non-Role to the List, and disallow that if asList has ever
278     * been called on this instance.
279     */
280
281    @Override
282    public boolean add(Object o) {
283        if (!tainted)
284            tainted = isTainted(o);
285        if (typeSafe)
286            checkTypeSafe(o);
287        return super.add(o);
288    }
289
290    @Override
291    public void add(int index, Object element) {
292        if (!tainted)
293            tainted = isTainted(element);
294        if (typeSafe)
295            checkTypeSafe(element);
296        super.add(index, element);
297    }
298
299    @Override
300    public boolean addAll(Collection<?> c) {
301        if (!tainted)
302            tainted = isTainted(c);
303        if (typeSafe)
304            checkTypeSafe(c);
305        return super.addAll(c);
306    }
307
308    @Override
309    public boolean addAll(int index, Collection<?> c) {
310        if (!tainted)
311            tainted = isTainted(c);
312        if (typeSafe)
313            checkTypeSafe(c);
314        return super.addAll(index, c);
315    }
316
317    @Override
318    public Object set(int index, Object element) {
319        if (!tainted)
320            tainted = isTainted(element);
321        if (typeSafe)
322            checkTypeSafe(element);
323        return super.set(index, element);
324    }
325
326    /**
327     * IllegalArgumentException if o is a non-Role object.
328     */
329    private static void checkTypeSafe(Object o) {
330        try {
331            o = (Role) o;
332        } catch (ClassCastException e) {
333            throw new IllegalArgumentException(e);
334        }
335    }
336
337    /**
338     * IllegalArgumentException if c contains any non-Role objects.
339     */
340    private static void checkTypeSafe(Collection<?> c) {
341        try {
342            Role r;
343            for (Object o : c)
344                r = (Role) o;
345        } catch (ClassCastException e) {
346            throw new IllegalArgumentException(e);
347        }
348    }
349
350    /**
351     * Returns true if o is a non-Role object.
352     */
353    private static boolean isTainted(Object o) {
354        try {
355            checkTypeSafe(o);
356        } catch (IllegalArgumentException e) {
357            return true;
358        }
359        return false;
360    }
361
362    /**
363     * Returns true if c contains any non-Role objects.
364     */
365    private static boolean isTainted(Collection<?> c) {
366        try {
367            checkTypeSafe(c);
368        } catch (IllegalArgumentException e) {
369            return true;
370        }
371        return false;
372    }
373}
374