001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.core;
021 
022 import js.*;
023 import js.core.*;
024 
025 /**
026  * <p>A utility class providing basic array operations with its static methods.</p>
027  * <p>Users are encouraged to use the utilities provided by this class instead of the 
028  * <b>opaque</b> methods of {@link js.ArrayLike} or {@link js.core.JsArray} in 
029  * consideration of the reuse benefit for re-compilation results.</p>
030  * 
031  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
032  */
033 public final class ArrayLikes extends Disposable
034 {
035     private ArrayLikes() {}
036 
037     /**
038      * <p>Deletes the specified element of an array instance.</p>
039      * @param arr The current array instance.
040      * @param i Array index of the element
041      * @return <tt>true</tt> if the deletion is successful, <tt>false</tt> otherwise.
042      * @see js.core.JsArray#delete(int)
043      * @since 1.0
044      */
045     public static final boolean delete(ArrayLike<?> arr, int i) {
046         return arr.delete(i);
047     }
048     /**
049      * <p>Gets the size of an array instance.</p>
050      * @param arr The current array instance.
051      * @return The size of the array
052      * @see js.core.JsArray#length()
053      * @since 1.0
054      */
055     public static final int length(ArrayObject<?> arr) {
056         return arr.length();
057     }
058     /**
059      * <p>Changes the size of an array instance.</p>
060      * @param arr The current array instance.
061      * @param length New size
062      * @return New size
063      * @see js.core.JsArray#length(int)
064      * @since 1.0
065      */
066     public static final int length(ArrayLike<?> arr, int length) {
067         return arr.length(length);
068     }
069     /**
070      * <p>Gets the <tt>index</tt> field of an array instance.</p>
071      * @param arr The current array instance.
072      * @return The value of the <tt>index</tt> field of the array object
073      * @see StringLikes#match(js.StringLike, RegExpLike)
074      * @see js.ArrayLike#index()
075      * @since 1.0
076      */
077     public static final Integer index(ArrayLike<?> arr) {
078         return arr.index();
079     }
080     /**
081      * <p>Gets the <tt>input</tt> field of an array instance.</p>
082      * @param arr The current array instance.
083      * @return The value of the <tt>input</tt> field of the array object
084      * @see StringLikes#match(js.StringLike, RegExpLike)
085      * @see RegExpLikes#exec(js.RegExpLike, Object)
086      * @see js.ArrayLike#input()
087      * @since 1.0
088      */
089     public static final String input(ArrayLike<?> arr) {
090         return arr.input();
091     }
092     /**
093      * <p>Creates and returns a new array object that is the result of concatenating its
094      * argument to the current array instance. This invocation does not modify the current 
095      * array. If the argument to <tt>concat</tt> is itself an array, the elements of that array 
096      * are concatenated, rather than the array itself.</p>
097      * @param arr The current array instance.
098      * @param arg A value to be concatenated with the current array.
099      * @return A new array object, which is formed by concatenating the specified argument
100      * to the current array.
101      * @see js.ArrayLike#concat(Object)
102      * @since 1.0
103      */
104     public static final <T> ArrayLike<T> concat(ArrayLike<T> arr, Object arg) {
105         return arr.concat(arg);
106     }
107     /**
108      * <p>Converts each element of the current array instance to a string and then 
109      * concatenates those strings, inserting a comma between the elements and returns 
110      * the resulting string.</p>
111      * @param arr The current array instance.
112      * @return The string that results from converting each element of the current array
113      * to a string and then concatenating them together with a comma between elements.
114      * @see #join(js.ArrayLike, Object)
115      * @see StringLikes#split(js.StringLike, Object)
116      * @see js.ArrayLike#join()
117      * @since 1.0
118      */
119     public static final String join(ArrayLike<?> arr) {
120         return arr.join();
121     }
122     /**
123      * <p>Converts each element of the current array instance to a string and then 
124      * concatenates those strings, inserting the separator string specified by 
125      * <tt>separator</tt> between the elements and returns the resulting string.</p>
126      * @param arr The current array instance.
127      * @param separator An optional string used to separate one element of the current array
128      * from the next in the returned string. If this argument is omitted, 
129      * <tt>undefined</tt> or <tt>null</tt>, a comma is used.
130      * @return The string that results from converting each element of the current array
131      * to a string and then concatenating them together, with the <tt>separator</tt>
132      * string between elements.
133      * @see #join(js.ArrayLike)
134      * @see StringLikes#split(js.StringLike, Object)
135      * @see js.ArrayLike#join(Object)
136      * @since 1.0
137      */
138     public static final String join(ArrayLike<?> arr, Object separator) {
139         return arr.join(separator);
140     }
141     /**
142      * <p>Deletes the last element of the current array instance, decrements the length of 
143      * the current array, and returns the value of the deleted element. If the current array is 
144      * already empty, this invocation does not change the array and returns the undefined <tt>null</tt> value.</p>
145      * @param arr The current array instance.
146      * @return The last element of the current array.
147      * @see js.ArrayLike#pop()
148      * @since 1.0
149      */
150     public static final <T> T pop(ArrayLike<T> arr) {
151         return arr.pop();
152     }
153     /**
154      * <p>Appends the argument to the end of the current array instance by modifying the 
155      * array directly rather than creating a new one.</p>
156      * @param arr The current array instance.
157      * @param v A value to be appended to the end of the current array.
158      * @return The new length of the array, after the specified value are appended to it.
159      * @see #push(js.ArrayLike, Object)
160      * @see js.ArrayLike#push(Object)
161      * @since 1.0
162      */
163     public static final int push(ArrayLike<?> arr, Object v) {
164         return arr.push(v);
165     }
166     /**
167      * <p>Reverses the order of the elements of the current array instance by rearranging 
168      * them in place without creating a new array. If there are multiple references to the 
169      * array, the new order of the array elements is visible through all references after 
170      * this invocation.</p>
171      * @param arr The current array instance.
172      * @see js.ArrayLike#reverse()
173      * @since 1.0
174      */
175     public static final void reverse(ArrayLike<?> arr) {
176         arr.reverse();
177     }
178     /**
179      * <p>Removes and returns the first element of the current array instance, shifting 
180      * all subsequent elements down one place to occupy the newly vacant space at the 
181      * start of the array. If the current array is empty, this invocation does nothing 
182      * and returns the undefined value <tt>null</tt>. Note that this invocation does 
183      * not create a new array; instead, it modifies the current array directly.</p>
184      * @param arr The current array instance.
185      * @return The former first element of the current array.
186      * @see #pop(js.ArrayLike)
187      * @see js.ArrayLike#shift()
188      * @since 1.0
189      */
190     public static final <T> T shift(ArrayLike<T> arr) {
191         return arr.shift();
192     }
193     /**
194      * <p>Returns a slice, or sub-array, of the current array instance without modifying 
195      * it. The returned array contains the element positioned by <tt>start</tt> and 
196      * all subsequent elements up to the end of the current array.</p>
197      * @param arr The current array instance.
198      * @param start The array index at which the slice is to begin. If negative, this 
199      * argument specifies a position measured from the end of the current array. That is, 
200      * -1 indicates the last element, -2 indicates the next from the last element, and so on.
201      * @return A new array that contains the elements of current array instance from the 
202      * element positioned by <tt>start</tt>, up to the end of the current array.
203      * @see #slice(js.ArrayLike, Object, Object)
204      * @see #splice(js.ArrayLike, Object)
205      * @see #splice(js.ArrayLike, Object, Object)
206      * @see #splice(js.ArrayLike, Object, Object, Object)
207      * @see js.ArrayLike#slice(Object)
208      * @since 1.0
209      */
210     public static final <T> ArrayLike<T> slice(ArrayLike<T> arr, Object start) {
211         return arr.slice(start);
212     }
213     /**
214      * <p>Returns a slice, or sub-array, of the current array instance without modifying 
215      * it. The returned array contains the element positioned by <tt>start</tt> and 
216      * all subsequent elements up to, but not including, the element positioned by 
217      * <tt>end</tt>. If <tt>end</tt> is an undefined value, the returned array 
218      * contains all elements from the <tt>start</tt> to the end of the current array.</p>
219      * @param arr The current array instance.
220      * @param start The array index at which the slice is to begin. If negative, this 
221      * argument specifies a position measured from the end of the current array. That is, 
222      * -1 indicates the last element, -2 indicates the next from the last element, and so on.
223      * @param end The array index immediately after the end of the slice. If undefined, 
224      * the slice includes all array elements from the <tt>start</tt> to the end 
225      * of the array. If this argument is negative, it specifies an array element measured 
226      * from the end of the array.
227      * @return A new array that contains the elements of current array instance from the 
228      * element positioned by <tt>start</tt>, up to, but not including, the element 
229      * positioned by <tt>end</tt>.
230      * @see #slice(js.ArrayLike, Object)
231      * @see #splice(js.ArrayLike, Object)
232      * @see #splice(js.ArrayLike, Object, Object)
233      * @see #splice(js.ArrayLike, Object, Object, Object)
234      * @see js.ArrayLike#slice(Object, Object)
235      * @since 1.0
236      */
237     public static final <T> ArrayLike<T> slice(ArrayLike<T> arr, Object start, Object end) {
238         return arr.slice(start, end);
239     }
240     /**
241      * <p>Sorts the elements of the current array instance in place by arranging them in 
242      * alphabetical order (more precisely, the order determined by the character encoding).
243      * To do this, elements are first converted to strings, if necessary, so that they can 
244      * be compared. Note that the array is sorted in place, and no copy is made.
245      * And undefined elements are always sorted to the end of the array.</p>
246      * @param arr The current array instance.
247      * @return A reference to the current array.
248      * @see #sort(js.ArrayLike, JsFunction)
249      * @see js.ArrayLike#sort()
250      * @since 1.0
251      */
252     public static final <T> ArrayLike<T> sort(ArrayLike<T> arr) {
253         return arr.sort();
254     }
255     /**
256      * <p>Sorts the elements of the current array instance with the custom ordering function 
257      * <tt>orderfunc</tt>. Note that the array is sorted in place, and no copy is made.
258      * And undefined elements are always sorted to the end of the array because undefined 
259      * values are never passed to the ordering function you supply.</p>
260      * @param arr The current array instance.
261      * @param orderfunc A comparison function that compares two values and returns a 
262      * number indicating their relative order. This function should take two arguments, 
263      * <tt>a</tt> and <tt>b</tt> for instance, and should return one of the following:
264      * <ul>
265      * <li>A value less than zero, if, according to your sort criteria, <tt>a</tt> is 
266      * less than <tt>b</tt> and should appear before <tt>b</tt> in the sorted 
267      * array.</li>
268      * <li>Zero, if <tt>a</tt> and <tt>b</tt> are equivalent for the purposes of 
269      * this sort.</li>
270      * <li>A value greater than zero, if <tt>a</tt> is greater than <tt>b</tt> 
271      * for the purposes of the sort.</li>
272      * </ul>
273      * @return A reference to the current array.
274      * @see #sort(js.ArrayLike)
275      * @see js.ArrayLike#sort(JsFunction)
276      * @since 1.0
277      */
278     public static final <T> ArrayLike<T> sort(ArrayLike<T> arr, JsFunction<? extends Number> orderfunc) {
279         return arr.sort(orderfunc);
280     }
281     /**
282      * <p>Deletes zero or more array elements starting with and including the element 
283      * positioned by <tt>start</tt>.</p>
284      * <p>Note that, this invocation modifies the current array directly.</p>
285      * @param arr The current array instance.
286      * @param start The array index at which the insertion and/or deletion is to begin.
287      * @return An array containing the elements, if any, deleted from the current array.
288      * @see #slice(js.ArrayLike, Object)
289      * @see #slice(js.ArrayLike, Object, Object)
290      * @see #splice(js.ArrayLike, Object, Object)
291      * @see #splice(js.ArrayLike, Object, Object, Object)
292      * @see js.ArrayLike#splice(Object)
293      * @since 1.0
294      */
295     public static final <T> ArrayLike<T> splice(ArrayLike<T> arr, Object start) {
296         return arr.splice(start);
297     }
298     /**
299      * <p>Deletes <tt>deleteCount</tt> elements of the current array instance starting 
300      * with and including the element positioned by <tt>start</tt>. Array elements 
301      * that appear after deletion are moved as necessary so that they remain contiguous 
302      * with the rest of the array.</p> 
303      * <p>Note that, this invocation modifies the current array directly.</p>
304      * @param arr The current array instance.
305      * @param start The array index at which the deletion is to begin.
306      * @param deleteCount The number of elements, starting with and including the element 
307      * positioned by <tt>start</tt>, to be deleted from the current array. If this 
308      * argument is undefined, this invocation deletes all elements from <tt>start</tt> to the end 
309      * of the array.
310      * @return An array containing the elements, if any, deleted from the current array.
311      * @see #slice(js.ArrayLike, Object)
312      * @see #slice(js.ArrayLike, Object, Object)
313      * @see #splice(js.ArrayLike, Object)
314      * @see #splice(js.ArrayLike, Object, Object, Object)
315      * @see js.ArrayLike#splice(Object, Object)
316      * @since 1.0
317      */
318     public static final <T> ArrayLike<T> splice(ArrayLike<T> arr, Object start, Object deleteCount) {
319         return arr.splice(start, deleteCount);
320     }
321     /**
322      * <p>Deletes <tt>deleteCount</tt> elements starting with and including the 
323      * element positioned by <tt>start</tt> and replaces them with the argument 
324      * <tt>value</tt>. Array elements that appear after the insertion or deletion are 
325      * moved as necessary so that they remain contiguous with the rest of the array.</p> 
326      * <p>Note that, this invocation modifies the current array directly.</p>
327      * @param arr The current array instance.
328      * @param start The array index at which the deletion and insertion is to begin.
329      * @param deleteCount The number of elements, starting with and including the element 
330      * positioned by <tt>start</tt>, to be deleted from the current array. If this 
331      * argument is undefined, this invocation deletes all elements from <tt>start</tt> to the end 
332      * of the array.
333      * @param value The value to be inserted into the current array, beginning at the index 
334      * specified by <tt>start</tt>.
335      * @return An array containing the elements, if any, deleted from the current array.
336      * @see #slice(js.ArrayLike, Object)
337      * @see #slice(js.ArrayLike, Object, Object)
338      * @see #splice(js.ArrayLike, Object)
339      * @see #splice(js.ArrayLike, Object, Object)
340      * @see js.ArrayLike#splice(Object, Object, Object)
341      * @since 1.0
342      */
343     public static final <T> ArrayLike<T> splice(ArrayLike<T> arr, Object start, Object deleteCount, Object value) {
344         return arr.splice(start, deleteCount, value);
345     }
346     /**
347      * <p>Inserts the argument at the beginning of the current array instance, 
348      * shifting the existing elements to higher indexes to make room. The argument becomes 
349      * the new element 0 of the array. Note that this invocation does not create a new 
350      * array; it modifies the current array directly.</p>
351      * @param arr The current array instance.
352      * @param arg A value that is inserted at the start of the current array.
353      * @return The new length of the current array.
354      * @see #shift(js.ArrayLike)
355      * @see js.ArrayLike#unshift(Object)
356      * @since 1.0
357      */
358     public static final int unshift(ArrayLike<?> arr, Object arg) {
359         return arr.unshift(arg);
360     }
361     /**
362      * <p>Appends an array element to an array.</p>
363      * @param arr The current array instance.
364      * @param arg A value that is added at the end of the current array.
365      * @return The current array.
366      * @see #push(ArrayLike, Object)
367      * @since 1.0
368      */
369     public static final <T> ArrayLike<T> add(ArrayLike<T> arr, T arg) {
370         arr.push(arg);
371         return arr;
372     }
373     /**
374      * <p>Removes the first element of an array and adds it to the end, shifting all the 
375      * other elements accordingly.</p>
376      * @param arr The current array instance.
377      * @return The current array.
378      * @see #shift(ArrayLike)
379      * @see #add(ArrayLike, Object)
380      * @since 1.0
381      */
382     public static final <T> ArrayLike<T> circulate(ArrayLike<T> arr) {
383         return add(arr, shift(arr));
384     }
385     /**
386      * <p>Returns the last element of an array.</p>
387      * @param arr The current array instance.
388      * @return The last element of the array.
389      * @see #length(ArrayObject)
390      * @since 1.0
391      */
392     public static final <T> T getLast(ArrayLike<T> arr) {
393         return arr.get(length(arr) - 1);
394     }
395 
396     public static final <T> ArrayLike<T> copy(ArrayLike<T> array) {
397         ArrayLike<T> ret = Js.array();
398         for (int i = 0, len = length(array); i < len; i++) {
399             T v = array.get(i);
400             if (Variables.defined(v)) {
401                 ret.set(i, v);
402             }
403         }
404         return ret;
405     }
406     /**
407      * <p>Inserts an element into the array immediately before the specified element 
408      * of this array. If the element being inserted is already in the array, it is 
409      * removed and reinserted at its new location.</p>
410      * @param arr The current array instance.
411      * @param arg The element to be inserted into the array.
412      * @param ref The element of this array before which <tt>arg</tt> is to be 
413      * inserted. If this argument is <tt>null</tt> or not in the array, this method 
414      * returns <tt>-1</tt>.
415      * @return The array index of the newly inserted element or <tt>-1</tt> if 
416      * <tt>ref</tt> is not found in the array.
417      * @see #indexOf(ArrayLike, Object)
418      * @see #remove(ArrayLike, Object)
419      * @since 1.0
420      */
421     public static final <T> int insert(ArrayLike<T> arr, T arg, T ref) {
422         remove(arr, arg);
423         int i = indexOf(arr, ref);
424         if (i != -1) {
425             splice(arr, i, 0, arg);
426         }
427         return i;
428     }
429     public static final int indexOf(ArrayLike<?> array, Object value) {
430         for (int i = 0, len = length(array); i < len; i++) {
431             if(Js.eq(array.get(i), value)) {
432                 return i;
433             }
434         }
435         return -1;
436     }
437     public static final int remove(ArrayLike<?> array, Object value) {
438         int index = indexOf(array, value);
439         if (index != -1) {
440             splice(array, index, 1);
441         }
442         return index;
443     }
444     public static final int removeAll(ArrayLike<?> array) {
445         int length = length(array);
446         if (length > 0) {
447             splice(array, 0, length);
448         }
449         return length;
450     }
451 }