001    // Copyright 2006-08 Regents of the University of California.  May be used 
002    // under the terms of the revised BSD license.  See LICENSING for details.
003    /** 
004     * @author Adrian Mettler 
005     */
006    package org.joe_e.array;
007    
008    import org.joe_e.JoeE;
009    import org.joe_e.Powerless;
010    import org.joe_e.reflect.Reflection;
011    
012    /**
013     * An immutable array containing powerless objects.
014     * 
015     * @param <E> the element type of objects contained in the array
016     */
017    public class PowerlessArray<E> extends ImmutableArray<E> implements Powerless {
018        static private final long serialVersionUID = 1L;
019    
020        /**
021         * Package-scope back-door constructor for use by subclasses that
022         * override all methods that make use of the field arr.  Nullity of arr is
023         * used to distinguish between instances with which this class must interact
024         * by using the public interface rather than through their arr field.
025         */
026            PowerlessArray(final Object[] arr) {
027                    super(arr); 
028            }
029        
030        /**
031         * Construct a <code>PowerlessArray</code>.  The type will be 
032         * @param values    each value, or an array of values
033         * @throws ClassCastException if the runtime component type of 
034         *     <code>values</code> is not powerless in the overlay type system
035         */
036        static public <T> PowerlessArray<T> array(final T... values) {
037            final Class<?> e = values.getClass().getComponentType();
038            if (!JoeE.isSubtypeOf(e, Powerless.class)) {
039                throw new ClassCastException(Reflection.getName(e) + 
040                                             " is not Powerless");
041            }
042            return new PowerlessArray<T>(values.clone());
043        }
044        
045        /* 
046         * See the comment in ConstArray for why the following methods exist.
047         */
048    
049        /**
050         * Construct an empty <code>ConstArray</code>.
051         */
052        static public <T> PowerlessArray<T> array() {
053            return new PowerlessArray<T>(new Object[]{});
054        }  
055        
056        /**
057         * Check an array to see that all elements are immutable; if so wraps
058         * it WITHOUT a defensive copy
059         */
060        static private <T> PowerlessArray<T> array2(final Object... values) {
061            for (Object v: values) {
062                Class<?> vType = v.getClass();
063                if (!JoeE.isSubtypeOf(vType, Powerless.class)) {
064                    throw new ClassCastException(Reflection.getName(vType) +
065                                                 " is not Powerless");
066                }
067            }
068            return new PowerlessArray<T>(values);
069        }
070    
071        /**
072         * Construct a <code>ConstArray</code> with one element.
073         * @param value    the value
074         */
075        static public <T> PowerlessArray<T> array(final T value) {
076            return array2(value);
077        }
078    
079        /**
080         * Construct a <code>ConstArray</code> with two elements.
081         * @param value1    the first value
082         * @param value2    the second value
083         */
084        static public <T> PowerlessArray<T> array(final T value1, final T value2) {
085            return array2(value1, value2);
086        }
087    
088        /**
089         * Construct an <code>PowerlessArray</code> with three elements.
090         * @param value1    the first value
091         * @param value2    the second value
092         * @param value3    the third value
093         */
094        static public <T> PowerlessArray<T> array(final T value1, final T value2, 
095                                              final T value3) {
096            return array2(value1, value2, value3);
097        }
098    
099        /**
100         * Construct an <code>PowerlessArray</code> with four elements.
101         * @param value1    the first value
102         * @param value2    the second value
103         * @param value3    the third value
104         * @param value4    the fourth value
105         */
106        static public <T> PowerlessArray<T> array(final T value1, final T value2, 
107                                              final T value3, final T value4) {
108            return array2(value1, value2, value3, value4);
109        }       
110        
111        /**
112         * Return a new <code>PowerlessArray</code> that contains the same elements
113         * as this one but with a new element added to the end.
114         * @param newE an element to add
115         * @return the new array
116         * @throws ClassCastException if <code>newE</code> is not powerless 
117         */
118        public PowerlessArray<E> with(E newE) {
119            if (!JoeE.instanceOf(newE, Powerless.class)) {
120                throw new ClassCastException(Reflection.getName(newE.getClass()) +
121                                             "is not Powerless");
122            }
123            // We use a new Object array here, because we don't know the static type
124            // of E that was used; it may not match the dynamic component type of
125            // arr due to array covariance.
126            final Object[] newArr = new Object[arr.length + 1];
127            System.arraycopy(arr, 0, newArr, 0, arr.length);
128            newArr[arr.length] = newE;
129            return new PowerlessArray<E>(newArr);
130        }
131        
132        /**
133         * Return a new <code>PowerlessArray</code> that contains the same elements
134         * as this one excluding the element at a specified index
135         * @param i the index of the element to exclude
136         * @return  the new array
137         */
138        public PowerlessArray<E> without(final int i) {
139            final Object[] newArr = new Object[arr.length - 1];
140            System.arraycopy(arr, 0, newArr, 0, i);
141            System.arraycopy(arr, i + 1, newArr, i, newArr.length - i);
142            return new PowerlessArray<E>(newArr);
143        }
144        
145        /**
146         * A {@link PowerlessArray} factory.
147         */
148        public static class Builder<E> extends ImmutableArray.Builder<E> {
149            /**
150             * Construct an instance with the default internal array length.
151             */
152            Builder() {
153                super();
154            }
155            
156            /**
157             * Construct an instance.
158             * @param estimate  estimated array length
159             */
160            Builder(int estimate) {
161                super(estimate);
162            }        
163    
164            /** 
165             * Appends an element to the Array
166             * @param newE the element to append
167             * @throws ClassCastException if the <code>newE</code> is not powerless
168             * @throws NegativeArraySizeException if the resulting internal array
169             *  would exceed the maximum length of a Java array.  The builder is
170             *  unmodified.
171             */
172             public void append(E newE) {
173                if (!JoeE.instanceOf(newE, Powerless.class)) {
174                    throw new ClassCastException(Reflection.getName(newE.getClass())
175                                                 + "is not Powerless");
176                }
177                
178                appendInternal(newE);
179            }
180    
181            /** 
182             * Appends all elements from a Java array to the Array
183             * @param newEs the element to append
184             * @throws ClassCastException if the <code>newEs</code> are not
185             *  powerless
186             * @throws IndexOutOfBoundsException if the resulting internal array
187             *  would exceed the maximum length of a Java array.  The builder is
188             *  unmodified.
189             */
190            public void append(E[] newEs) {
191                append(newEs, 0, newEs.length);
192            }
193    
194            /** 
195             * Appends a range of elements from a Java array to the Array
196             * @param newEs the source array
197             * @param off   the index of the first element to append
198             * @param len   the number of elements to append
199             * @throws ClassCastException if the <code>newEs</code> is not powerless
200             * @throws IndexOutOfBoundsException if an out-of-bounds index would
201             *  be referenced or the resulting internal array would exceed the
202             *  maximum length of a Java array.  The builder is unmodified.
203             */
204            public void append(E[] newEs, int off, int len) {
205                final Class<?> e = newEs.getClass().getComponentType();
206                if (!JoeE.isSubtypeOf(e, Powerless.class)) {
207                    throw new ClassCastException(Reflection.getName(e) +
208                                                 " is not Powerless");
209                }
210                
211                appendInternal(newEs, off, len);
212            }
213            
214            /**
215             * Create a snapshot of the current content.
216             * @return a <code>PowerlesstArray<E></code> containing the elements so far
217             */
218            public PowerlessArray<E> snapshot() {
219                final Object[] arr;
220                if (size == buffer.length) {
221                    arr = buffer;
222                } else {
223                    arr = new Object[size];
224                    System.arraycopy(buffer, 0, arr, 0, size);
225                }
226                return new PowerlessArray<E>(arr);
227            }
228        } 
229        
230        /**
231         * Get a <code>PowerlessArray.Builder</code>.  This is equivalent to the
232         * constructor.
233         * @return a new builder instance, with the default internal array length
234         */
235        public static <E> Builder<E> builder() {
236            return new Builder<E>(0);
237        }
238        
239        /**
240         * Get a <code>PowerlessArray.Builder</code>.  This is equivalent to the
241         * constructor.
242         * @param estimate  estimated array length  
243         * @return a new builder instance
244         */    
245        public static <E> Builder<E> builder(final int estimate) {
246            return new Builder<E>(estimate);
247        }
248    }