| JavaDoq: CArray.java |
001
002 /*
003 * Descripter 1.0 - Java Script Engines
004 * Copyright (C) 2010-2015 Jianjun Liu (J.J.Liu)
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 org.descripter.js.api.core;
021
022 import java.util.AbstractList;
023 import java.util.Collections;
024 import java.util.Comparator;
025 import java.util.List;
026
027 import org.descripter.js.api.Function;
028 import org.descripter.js.api.Key;
029
030 /**
031 * <p>Emulates JavaScript Array objects.</p>
032 *
033 * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.descripter.org" target="_blank">http://www.descripter.org</a>
034 * @since Descripter 1.0
035 */
036 public class CArray extends CObject
037 {
038 /**
039 * <p>Constructs a {@link CObject} context of this type.</p>
040 * @param constructor The constructor {@link Function} object.
041 * @since Descripter 1.0
042 */
043 public CArray(Function<?> constructor) {
044 super(constructor);
045 }
046
047 /**
048 * <p>Constructs a {@link CObject} context of this type.</p>
049 * @param constructor The constructor {@link Function} object.
050 * @param length The length of the array being constructed
051 * @since Descripter 1.0
052 */
053 public CArray(Function<?> constructor, int length) {
054 this(constructor);
055 length(length);
056 }
057
058 /**
059 * <p>Constructs a {@link CObject} context of this type.</p>
060 * @param constructor The constructor {@link Function} object.
061 * @param array An array of initial elements for the array being constructed
062 * @since Descripter 1.0
063 */
064 public CArray(Function<?> constructor, Object ...array) {
065 this(constructor, array.length);
066 for (int i = 0; i < array.length; i++) {
067 Object o = array[i];
068 if (o != null) {
069 super.put(i, o);
070 }
071 }
072 }
073
074 /**
075 * <p>Returns a string representation of the current object.</p>
076 * @return The string representation of the current object
077 * @since Descripter 1.0
078 */
079 @Override
080 public final String toString() {
081 StringBuilder sb = new StringBuilder("[");
082 for (int i = 0, length = length(); i < length; i++) {
083 if (i > 0) {
084 sb.append(',');
085 }
086 Object o = get(i);
087 if (o instanceof String) {
088 sb.append('"');
089 sb.append(o);
090 sb.append('"');
091
092 } else {
093 sb.append(o);
094 }
095 }
096 sb.append(']');
097 return sb.toString();
098 }
099
100 /**
101 * <p>Returns the length of the current array.</p>
102 * @return The length of the current array
103 * @since Descripter 1.0
104 */
105 public final int length() {
106 return intValue(getNumber(core()._length));
107 }
108
109 /**
110 * <p>Sets the length of the current array.</p>
111 * @param length The new length for the current array
112 * @since Descripter 1.0
113 */
114 public final void length(int length) {
115 if (length < 0) {
116 length = 0;
117 }
118 for (int i = length, len = length(); i < len; i++) {
119 hide(i);
120 }
121 super.put(core()._length, length);
122 }
123
124 /**
125 * <p>Gets the <tt>callee</tt> {@link Function} property of the current array.</p>
126 * @return The <tt>callee</tt> {@link Function} property of the current array
127 * @since Descripter 1.0
128 */
129 public final Function<?> callee() {
130 return (Function<?>)get(core()._callee);
131 }
132
133 /**
134 * <p>Gets the <tt>index</tt> property of the current array.</p>
135 * @return The <tt>index</tt> property of the current array
136 * @since Descripter 1.0
137 */
138 public final int index() {
139 return getNumber(core()._index).intValue();
140 }
141
142 /**
143 * <p>Gets the <tt>input</tt> property of the current array.</p>
144 * @return The <tt>input</tt> property of the current array
145 * @since Descripter 1.0
146 */
147 public final String input() {
148 return getString(core()._index);
149 }
150
151 /**
152 * <p>Sets the value associated with the specified key.</p>
153 * @param key A {@link Key} to set the value
154 * @param val The value to set
155 * @throws RuntimeException if the current context is read-only.
156 * @since Descripter 1.0
157 */
158 @Override
159 public final void put(Key key, Object val) {
160 if (key.equals(core()._length)) {
161 length(intValue(val));
162 } else {
163 if (key.hashCode() >= length()) {
164 length(key.hashCode() + 1);
165 }
166 super.put(key, val);
167 }
168 }
169
170 /**
171 * <p>Creates and returns a new array object that is the result of concatenating the
172 * arguments to the current array instance. This invocation does not modify the current
173 * array.</p>
174 * @param args An array of values to be concatenated with the current array.
175 * @return A new array object, which is formed by concatenating the specified arguments
176 * to the current array.
177 * @since Descripter 1.0
178 */
179 public final CArray concat(CArray args) {
180 CArray array = slice(0);
181 for (int j = 0, length = args.length(); j < length; j++) {
182 Object o = args.get(j);
183 if (o instanceof CArray) {
184 CArray a = (CArray)o;
185 for (int i = 0, len = a.length(); i < len; i++) {
186 array.put(array.length(), a.get(i));
187 }
188 } else {
189 array.put(array.length(), o);
190 }
191 }
192 return array;
193 }
194
195 /**
196 * <p>Converts each element of the current array instance to a string and then
197 * concatenates those strings, inserting a comma between the elements and returns
198 * the resulting string.</p>
199 * @return The string that results from converting each element of the current array
200 * to a string and then concatenating them together with a comma between elements.
201 * @see #join(String)
202 * @since Descripter 1.0
203 */
204 public final String join() {
205 return join("");
206 }
207
208 /**
209 * <p>Converts each element of the current array instance to a string and then
210 * concatenates those strings, inserting the separator string specified by
211 * <tt>separator</tt> between the elements and returns the resulting string.</p>
212 * @param separator An optional string used to separate one element of the current array
213 * from the next in the returned string. If this argument is omitted,
214 * <tt>undefined</tt> or <tt>null</tt>, a comma is used.
215 * @return The string that results from converting each element of the current array
216 * to a string and then concatenating them together, with the <tt>separator</tt>
217 * string between elements.
218 * @see #join()
219 * @since Descripter 1.0
220 */
221 public final String join(String separator) {
222 if (separator == null) {
223 return join();
224 }
225 StringBuilder sb = new StringBuilder();
226 for (int i = 0, length = length(); i < length; i++) {
227 if (i > 0) {
228 sb.append(separator);
229 }
230 sb.append(get(i).toString());
231 }
232 return sb.toString();
233 }
234
235 /**
236 * <p>Deletes the last element of the current array instance, decrements the length of
237 * the current array, and returns the value of the deleted element. If the current array is
238 * already empty, this invocation does not change the array and returns the undefined <tt>null</tt> value.</p>
239 * @return The last element of the current array.
240 * @since Descripter 1.0
241 */
242 public final Object pop() {
243 int last = length() - 1;
244 Object o = get(last);
245 length(last);
246 return o;
247 }
248
249 /**
250 * <p>Appends the arguments to the end of the current array instance by modifying the
251 * array directly rather than creating a new one.</p>
252 * @param args An array of values to be appended to the end of the current array.
253 * @return The new length of the array, after the specified value are appended to it.
254 * @since Descripter 1.0
255 */
256 public final int push(CArray args) {
257 for (int i = 0, length = args.length(); i < length; i++) {
258 put(length(), args.get(i));
259 }
260 return length();
261 }
262
263 /**
264 * <p>Reverses the order of the elements of the current array instance by rearranging
265 * them in place without creating a new array. If there are multiple references to the
266 * array, the new order of the array elements is visible through all references after
267 * this invocation.</p>
268 * @return The reversed array
269 * @since Descripter 1.0
270 */
271 public final CArray reverse() {
272 CArray array = new CArray(constructor);
273 for (int i = length() - 1; i >= 0; i--) {
274 array.put(array.length(), get(i));
275 }
276 return array;
277 }
278
279 private final void move(int start, int count) {
280 int length = length();
281 if (count < 0) {
282 for (int i = start, j = i - count; i < length; i++, j++) {
283 put(i, get(j));
284 }
285 length(length - count);
286 } else if (count > 0) {
287 for (int i = length - 1, j = i + count; i >= start; i--, j--) {
288 put(j, get(i));
289 }
290 }
291 }
292
293 /**
294 * <p>Removes and returns the first element of the current array instance, shifting
295 * all subsequent elements down one place to occupy the newly vacant space at the
296 * start of the array. If the current array is empty, this invocation does nothing
297 * and returns the undefined value <tt>null</tt>. Note that this invocation does
298 * not create a new array; instead, it modifies the current array directly.</p>
299 * @return The former first element of the current array.
300 * @see #pop()
301 * @since Descripter 1.0
302 */
303 public final Object shift() {
304 Object o = get(0);
305 move(1, -1);
306 return o;
307 }
308
309 /**
310 * <p>Returns a slice, or sub-array, of the current array instance without modifying
311 * it. The returned array contains the element positioned by the first value of the
312 * argument list and all subsequent elements up to, but not including, the element
313 * positioned by the second value of the argument list. If the second value is not
314 * specified or undefined, the returned array contains all elements from the position
315 * specified by the first value to the end of the current array.</p>
316 * @param args A list of the argument values. The first value specifies the array
317 * index at which the slice is to begin. If this value is negative, it specifies a
318 * position measured from the end of the current array. That is, -1 indicates the
319 * last element, -2 indicates the next from the last element, and so on. The second
320 * value specifies the array index immediately after the end of the slice. If it is
321 * undefined or not specified, the slice includes all array elements from the position
322 * specified by the first value to the end of the array. If the second value is
323 * negative, it specifies the array element measured from the end of the array.
324 * @return A new array that contains the elements of current array instance from the
325 * element positioned by the first value of <tt>args</tt>, up to, but not including,
326 * the element positioned by the second value of <tt>args</tt>.
327 * @see #slice(int)
328 * @see #slice(int, int)
329 * @see #splice(CArray)
330 * @see #splice(int)
331 * @see #splice(int, int)
332 * @see #splice(int, int, CArray)
333 * @since Descripter 1.0
334 */
335 public final CArray slice(CArray args) {
336 switch (args.length()) {
337 case 1:
338 return slice(intValue(args.get(0)));
339 case 2:
340 return slice(intValue(args.get(0)), intValue(args.get(1)));
341 default:
342 return null;
343 }
344 }
345
346 /**
347 * <p>Returns a slice, or sub-array, of the current array instance without modifying
348 * it. The returned array contains the element positioned by <tt>start</tt> and
349 * all subsequent elements up to the end of the current array.</p>
350 * @param start The array index at which the slice is to begin. If negative, this
351 * argument specifies a position measured from the end of the current array. That is,
352 * -1 indicates the last element, -2 indicates the next from the last element, and so on.
353 * @return A new array that contains the elements of current array instance from the
354 * element positioned by <tt>start</tt>, up to the end of the current array.
355 * @see #slice(CArray)
356 * @see #slice(int, int)
357 * @see #splice(CArray)
358 * @see #splice(int)
359 * @see #splice(int, int)
360 * @see #splice(int, int, CArray)
361 * @since Descripter 1.0
362 */
363 public final CArray slice(int start) {
364 return slice(start, length());
365 }
366
367 /**
368 * <p>Returns a slice, or sub-array, of the current array instance without modifying
369 * it. The returned array contains the element positioned by <tt>start</tt> and
370 * all subsequent elements up to, but not including, the element positioned by
371 * <tt>end</tt>. If <tt>end</tt> is an undefined value, the returned array
372 * contains all elements from the <tt>start</tt> to the end of the current array.</p>
373 * @param start The array index at which the slice is to begin. If negative, this
374 * argument specifies a position measured from the end of the current array. That is,
375 * -1 indicates the last element, -2 indicates the next from the last element, and so on.
376 * @param end The array index immediately after the end of the slice. If undefined,
377 * the slice includes all array elements from the <tt>start</tt> to the end
378 * of the array. If this argument is negative, it specifies an array element measured
379 * from the end of the array.
380 * @return A new array that contains the elements of current array instance from the
381 * element positioned by <tt>start</tt>, up to, but not including, the element
382 * positioned by <tt>end</tt>.
383 * @see #slice(CArray)
384 * @see #slice(int)
385 * @see #splice(CArray)
386 * @see #splice(int)
387 * @see #splice(int, int)
388 * @see #splice(int, int, CArray)
389 * @since Descripter 1.0
390 */
391 public final CArray slice(int start, int end) {
392 int length = length();
393 if (start < 0) {
394 start += length;
395 }
396 if (end < 0) {
397 end += length;
398 }
399 CArray array = new CArray(constructor);
400 for (int i = start, j = 0; i < end; i++, j++) {
401 if (has(i)) {
402 array.put(j, get(i));
403 }
404 }
405 return array;
406 }
407
408 /**
409 * <p>Sorts the elements of the current array instance in place by arranging them in
410 * alphabetical order (more precisely, the order determined by the character encoding).
411 * To do this, elements are first converted to strings, if necessary, so that they can
412 * be compared. Note that the array is sorted in place, and no copy is made.
413 * And undefined elements are always sorted to the end of the array.</p>
414 * @return A reference to the current array.
415 * @see #sort(Function)
416 * @since Descripter 1.0
417 */
418 public final CArray sort() {
419 Collections.sort(list());
420 return this;
421 }
422
423 private List<Element> list() {
424 return new AbstractList<Element>() {
425 @Override
426 public Element get(int index) {
427 return new Element(CArray.this.get(index));
428 }
429
430 @Override
431 public Element set(int index, Element element) {
432 CArray.this.put(index, element.value);
433 return element;
434 }
435
436 @Override
437 public int size() {
438 return CArray.this.length();
439 }
440 };
441 }
442
443 private static class Element implements Comparable<Element>
444 {
445 public final Object value;
446
447 public Element(Object value) {
448 this.value = value;
449 }
450
451 @Override
452 public int compareTo(Element e) {
453 return toString().compareTo(e.toString());
454 }
455
456 @Override
457 public String toString() {
458 return CArray.toString(value);
459 }
460 }
461
462 /**
463 * <p>Sorts the elements of the current array instance with the custom ordering function
464 * <tt>orderer</tt>. Note that the array is sorted in place, and no copy is made.
465 * And undefined elements are always sorted to the end of the array because undefined
466 * values are never passed to the ordering function you supply.</p>
467 * @param orderer A comparison function that compares two values and returns a
468 * number indicating their relative order. This function should take two arguments,
469 * <tt>a</tt> and <tt>b</tt> for instance, and should return one of the following:
470 * <ul>
471 * <li>A value less than zero, if, according to your sort criteria, <tt>a</tt> is
472 * less than <tt>b</tt> and should appear before <tt>b</tt> in the sorted
473 * array.</li>
474 * <li>Zero, if <tt>a</tt> and <tt>b</tt> are equivalent for the purposes of
475 * this sort.</li>
476 * <li>A value greater than zero, if <tt>a</tt> is greater than <tt>b</tt>
477 * for the purposes of the sort.</li>
478 * </ul>
479 * @return A reference to the current array.
480 * @see #sort()
481 * @since Descripter 1.0
482 */
483 public final CArray sort(final Function<?> orderer) {
484 if (orderer == null) {
485 return sort();
486 }
487 Collections.sort(
488 list(),
489 new Comparator<Element>() {
490 @Override
491 public int compare(Element e1, Element e2) {
492 return intValue(core().call(orderer, e1.value, e2.value));
493 }
494 }
495 );
496 return this;
497 }
498
499 /**
500 * <p>Deletes elements, numbered by the second value of <tt>args</tt>, starting with and
501 * including the element positioned by the first value of <tt>args</tt>, and replaces
502 * them with the values listed by <tt>args</tt> from the third value. Array elements
503 * that appear after the insertion or deletion are moved as necessary so that they
504 * remain contiguous with the rest of the array.</p>
505 * <p>Note that, this invocation modifies the current array directly.</p>
506 * @param args A list of the argument values. The first value specifies the array
507 * index at which the deletion and insertion is to begin. The second value specifies
508 * the number of elements, starting with and including the element positioned by the
509 * first value, to be deleted from the current array. If the second value is undefined,
510 * this invocation deletes all elements from the position specified by the first value
511 * to the end of the array. The rest of the list provides the values to be inserted
512 * into the current array, beginning at the position specified by the first value.
513 * @return An array containing the elements, if any, deleted from the current array.
514 * @see #slice(CArray)
515 * @see #slice(int)
516 * @see #slice(int, int)
517 * @see #splice(int)
518 * @see #splice(int, int)
519 * @see #splice(int, int, CArray)
520 * @since Descripter 1.0
521 */
522 public final CArray splice(CArray args) {
523 switch (args.length()) {
524 case 1:
525 return splice(intValue(args.get(0)));
526 case 2:
527 return splice(intValue(args.get(0)), intValue(args.get(1)));
528 case 3:
529 return splice(intValue(args.get(0)), intValue(args.get(1)), (CArray)args.get(2));
530 default:
531 return null;
532 }
533 }
534
535 /**
536 * <p>Deletes zero or more array elements starting with and including the element
537 * positioned by <tt>start</tt>.</p>
538 * <p>Note that, this invocation modifies the current array directly.</p>
539 * @param start The array index at which the insertion and/or deletion is to begin.
540 * @return An array containing the elements, if any, deleted from the current array.
541 * @see #slice(CArray)
542 * @see #slice(int)
543 * @see #slice(int, int)
544 * @see #splice(CArray)
545 * @see #splice(int, int)
546 * @see #splice(int, int, CArray)
547 * @since Descripter 1.0
548 */
549 public final CArray splice(int start) {
550 return splice(start, length());
551 }
552
553 /**
554 * <p>Deletes <tt>deleteCount</tt> elements of the current array instance starting
555 * with and including the element positioned by <tt>start</tt>. Array elements
556 * that appear after deletion are moved as necessary so that they remain contiguous
557 * with the rest of the array.</p>
558 * <p>Note that, this invocation modifies the current array directly.</p>
559 * @param start The array index at which the deletion is to begin.
560 * @param deleteCount The number of elements, starting with and including the element
561 * positioned by <tt>start</tt>, to be deleted from the current array. If this
562 * argument is undefined, this invocation deletes all elements from <tt>start</tt> to the end
563 * of the array.
564 * @return An array containing the elements, if any, deleted from the current array.
565 * @see #slice(CArray)
566 * @see #slice(int)
567 * @see #slice(int, int)
568 * @see #splice(CArray)
569 * @see #splice(int)
570 * @see #splice(int, int, CArray)
571 * @since Descripter 1.0
572 */
573 public final CArray splice(int start, int deleteCount) {
574 CArray array = slice(start, start + deleteCount);
575 move(start, - deleteCount);
576 return array;
577 }
578
579 /**
580 * <p>Deletes <tt>deleteCount</tt> elements starting with and including the
581 * element positioned by <tt>start</tt> and replaces them with the argument
582 * <tt>values</tt>. Array elements that appear after the insertion or deletion are
583 * moved as necessary so that they remain contiguous with the rest of the array.</p>
584 * <p>Note that, this invocation modifies the current array directly.</p>
585 * @param start The array index at which the deletion and insertion is to begin.
586 * @param deleteCount The number of elements, starting with and including the element
587 * positioned by <tt>start</tt>, to be deleted from the current array. If this
588 * argument is undefined, this invocation deletes all elements from <tt>start</tt> to the end
589 * of the array.
590 * @param values The array of values to be inserted into the current array, beginning at the index
591 * specified by <tt>start</tt>.
592 * @return An array containing the elements, if any, deleted from the current array.
593 * @see #slice(CArray)
594 * @see #slice(int)
595 * @see #slice(int, int)
596 * @see #splice(CArray)
597 * @see #splice(int)
598 * @see #splice(int, int)
599 * @since Descripter 1.0
600 */
601 public final CArray splice(int start, int deleteCount, CArray values) {
602 CArray array = slice(start, start + deleteCount);
603 move(start, values.length() - deleteCount);
604 for (int i = start, j = 0; j < values.length(); i++, j++) {
605 put(i, values.get(j));
606 }
607 return array;
608 }
609
610 /**
611 * <p>Inserts the arguments at the beginning of the current array instance,
612 * shifting the existing elements to higher indexes to make room. The first argument becomes
613 * the new element 0 of the array. Note that this invocation does not create a new
614 * array; it modifies the current array directly.</p>
615 * @param values An array of values that are inserted at the start of the current array.
616 * @return The new length of the current array.
617 * @see #shift()
618 * @since Descripter 1.0
619 */
620 public final int unshift(CArray values) {
621 splice(0, 0, values);
622 return length();
623 }
624 }
| JavaDoq: CArray.java |