KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > ArrayModule


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.quercus.lib;
31
32 import com.caucho.quercus.QuercusModuleException;
33 import com.caucho.quercus.annotation.Optional;
34 import com.caucho.quercus.annotation.ReadOnly;
35 import com.caucho.quercus.annotation.Reference;
36 import com.caucho.quercus.annotation.UsesSymbolTable;
37 import com.caucho.quercus.env.*;
38 import com.caucho.quercus.env.ArrayValue.AbstractGet;
39 import com.caucho.quercus.env.ArrayValue.GetKey;
40 import com.caucho.quercus.env.ArrayValue.KeyComparator;
41 import com.caucho.quercus.env.ArrayValue.ValueComparator;
42 import com.caucho.quercus.module.AbstractQuercusModule;
43 import com.caucho.quercus.program.AbstractFunction;
44 import com.caucho.util.L10N;
45 import com.caucho.util.RandomUtil;
46
47 import java.text.Collator JavaDoc;
48 import java.util.Comparator JavaDoc;
49 import java.util.Iterator JavaDoc;
50 import java.util.Locale JavaDoc;
51 import java.util.Map JavaDoc;
52 import java.util.logging.Level JavaDoc;
53 import java.util.logging.Logger JavaDoc;
54
55 /**
56  * PHP array routines.
57  */

58 public class ArrayModule
59   extends AbstractQuercusModule
60 {
61   private static final L10N L = new L10N(ArrayModule.class);
62
63   private static final Logger JavaDoc log =
64     Logger.getLogger(ArrayModule.class.getName());
65
66   public static final int CASE_UPPER = 2;
67   public static final int CASE_LOWER = 1;
68
69   public static final int SORT_REGULAR = 0;
70   public static final int SORT_NUMERIC = 1;
71   public static final int SORT_STRING = 2;
72   public static final int SORT_LOCALE_STRING = 5;
73   public static final int SORT_NORMAL = 1;
74   public static final int SORT_REVERSE = -1;
75
76   public static final int SORT_DESC = 3;
77   public static final int SORT_ASC = 4;
78   
79   public static final int EXTR_OVERWRITE = 0;
80   public static final int EXTR_SKIP = 1;
81   public static final int EXTR_PREFIX_SAME = 2;
82   public static final int EXTR_PREFIX_ALL = 3;
83   public static final int EXTR_PREFIX_INVALID = 4;
84   public static final int EXTR_IF_EXISTS = 6;
85   public static final int EXTR_PREFIX_IF_EXISTS = 5;
86   public static final int EXTR_REFS = 256;
87
88   public static final boolean CASE_SENSITIVE = true;
89   public static final boolean CASE_INSENSITIVE = false;
90   public static final boolean KEY_RESET = true;
91   public static final boolean NO_KEY_RESET = false;
92   public static final boolean STRICT = true;
93   public static final boolean NOT_STRICT = false;
94
95   private static final CompareString CS_VALUE_NORMAL
96     = new CompareString(ArrayValue.GET_VALUE, SORT_NORMAL);
97   private static final CompareString CS_VALUE_REVERSE
98     = new CompareString(ArrayValue.GET_VALUE, SORT_REVERSE);
99   private static final CompareString CS_KEY_NORMAL
100     = new CompareString(ArrayValue.GET_KEY, SORT_NORMAL);
101   private static final CompareString CS_KEY_REVERSE
102     = new CompareString(ArrayValue.GET_KEY, SORT_REVERSE);
103
104   private static final CompareNumeric CN_VALUE_NORMAL
105     = new CompareNumeric(ArrayValue.GET_VALUE, SORT_NORMAL);
106   private static final CompareNumeric CN_VALUE_REVERSE
107     = new CompareNumeric(ArrayValue.GET_VALUE, SORT_REVERSE);
108   private static final CompareNumeric CN_KEY_NORMAL
109     = new CompareNumeric(ArrayValue.GET_KEY, SORT_NORMAL);
110   private static final CompareNumeric CN_KEY_REVERSE
111     = new CompareNumeric(ArrayValue.GET_KEY, SORT_REVERSE);
112
113   private static final CompareNormal CNO_VALUE_NORMAL
114     = new CompareNormal(ArrayValue.GET_VALUE, SORT_NORMAL);
115   private static final CompareNormal CNO_VALUE_REVERSE
116     = new CompareNormal(ArrayValue.GET_VALUE, SORT_REVERSE);
117   private static final CompareNormal CNO_KEY_NORMAL
118     = new CompareNormal(ArrayValue.GET_KEY, SORT_NORMAL);
119   private static final CompareNormal CNO_KEY_REVERSE
120     = new CompareNormal(ArrayValue.GET_KEY, SORT_REVERSE);
121
122   private static final CompareNatural CNA_VALUE_NORMAL_SENSITIVE
123     = new CompareNatural(ArrayValue.GET_VALUE, SORT_NORMAL, CASE_SENSITIVE);
124   private static final CompareNatural CNA_VALUE_NORMAL_INSENSITIVE
125     = new CompareNatural(ArrayValue.GET_VALUE, SORT_NORMAL, CASE_INSENSITIVE);
126
127   /**
128    * Returns true for the mysql extension.
129    */

130   public String JavaDoc []getLoadedExtensions()
131   {
132     return new String JavaDoc[] { "standard" };
133   }
134
135   /**
136    * Changes the key case
137    */

138   public Value array_change_key_case(ArrayValue array,
139                                      @Optional("CASE_LOWER") int toCase)
140   {
141     if (array == null)
142       return BooleanValue.FALSE;
143
144     ArrayValue newArray = new ArrayValueImpl();
145
146     for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
147       Value keyValue = entry.getKey();
148
149       if (keyValue instanceof StringValue) {
150         String JavaDoc key = keyValue.toString();
151
152         if (toCase == CASE_UPPER)
153           key = key.toUpperCase();
154         else
155           key = key.toLowerCase();
156
157         newArray.put(new StringValueImpl(key), entry.getValue());
158       }
159       else
160         newArray.put(keyValue, entry.getValue());
161     }
162
163     return newArray;
164   }
165
166   /**
167    * Chunks the array
168    */

169   public Value array_chunk(Env env,
170                            ArrayValue array,
171                            int size,
172                            @Optional boolean preserveKeys)
173   {
174     if (array == null)
175       return NullValue.NULL;
176
177     ArrayValue newArray = new ArrayValueImpl();
178     ArrayValue currentArray = null;
179
180     if (size < 1) {
181       env.warning("Size parameter expected to be greater than 0");
182
183       return NullValue.NULL;
184     }
185
186     int i = 0;
187     for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
188       Value key = entry.getKey();
189       Value value = entry.getKey();
190
191       if (i % size == 0) {
192         currentArray = new ArrayValueImpl();
193         newArray.put(currentArray);
194       }
195
196       if (preserveKeys)
197         currentArray.put(key, value);
198       else
199         currentArray.put(new LongValue(i % size), value);
200
201       i++;
202     }
203
204     return newArray;
205   }
206
207   /**
208    * Combines array
209    */

210   public Value array_combine(Env env, ArrayValue keys,
211                              ArrayValue values)
212   {
213     if (keys == null || values == null)
214       return BooleanValue.FALSE;
215
216     if (keys.getSize() < 1 || values.getSize() < 1) {
217       env.warning("Both parameters should have at least 1 element");
218
219       return BooleanValue.FALSE;
220     }
221
222     if (keys.getSize() != values.getSize()) {
223       env.warning("Both parameters should have equal number of elements");
224
225       return BooleanValue.FALSE;
226     }
227
228     Iterator JavaDoc<Value> keyIter = keys.values().iterator();
229     Iterator JavaDoc<Value> valueIter = values.values().iterator();
230
231     ArrayValue array = new ArrayValueImpl();
232
233     while (keyIter.hasNext() && valueIter.hasNext()) {
234       array.put(keyIter.next(), valueIter.next());
235     }
236
237     return array;
238   }
239
240   /**
241    * Counts the values
242    */

243   public Value array_count_values(Env env, ArrayValue array)
244   {
245     if (array == null)
246       return NullValue.NULL;
247
248     ArrayValue result = new ArrayValueImpl();
249
250     for (Value value : array.values()) {
251       if (! (value instanceof LongValue) && ! (value instanceof StringValue))
252         env.warning("Can only count STRING and INTEGER values!");
253       else {
254         Value count = result.get(value);
255
256         if (count == null)
257           count = new LongValue(1);
258         else
259           count = count.add(1);
260
261         result.put(value, count);
262       }
263     }
264
265     return result;
266   }
267
268   /**
269    * Pops off the top element
270    */

271   public Value array_pop(Value value)
272   {
273     if (value instanceof ArrayValue) {
274       ArrayValue array = (ArrayValue) value;
275
276       return array.pop();
277     }
278     else
279       return BooleanValue.FALSE;
280   }
281
282   /**
283    * Returns the size of the array.
284    */

285   public static Value count(@ReadOnly Value value)
286   {
287     return new LongValue(value.getSize());
288   }
289
290   /**
291    * Returns the current value of the array.
292    */

293   public static Value current(@ReadOnly Value value)
294   {
295     if (value instanceof ArrayValue) {
296       ArrayValue array = (ArrayValue) value;
297
298       return array.current();
299     }
300     else
301       return BooleanValue.FALSE;
302   }
303
304   /**
305    * Returns the current key of the array.
306    */

307   public static Value key(@ReadOnly Value value)
308   {
309     if (value instanceof ArrayValue) {
310       ArrayValue array = (ArrayValue) value;
311
312       return array.key();
313     }
314     else
315       return BooleanValue.FALSE;
316   }
317
318   /**
319    * Returns the current value of the array.
320    */

321   public static Value pos(@ReadOnly Value value)
322   {
323     return current(value);
324   }
325
326   /**
327    * Returns the next value of the array.
328    */

329   public static Value next(Value value)
330   {
331     if (value instanceof ArrayValue) {
332       ArrayValue array = (ArrayValue) value;
333
334       return array.next();
335     }
336     else
337       return BooleanValue.FALSE;
338   }
339
340   /**
341    * Returns the next value of the array.
342    */

343   public static Value each(Value value)
344   {
345     if (value instanceof ArrayValue) {
346       ArrayValue array = (ArrayValue) value;
347
348       return array.each();
349     }
350     else
351       return BooleanValue.FALSE;
352   }
353
354   /**
355    * Returns the previous value of the array.
356    */

357   public static Value prev(Value value)
358   {
359     if (value instanceof ArrayValue) {
360       ArrayValue array = (ArrayValue) value;
361
362       return array.prev();
363     }
364     else
365       return BooleanValue.FALSE;
366   }
367
368   /**
369    * Resets the pointer
370    */

371   public static Value reset(Value value)
372   {
373     if (value instanceof ArrayValue) {
374       ArrayValue array = (ArrayValue) value;
375
376       return array.reset();
377     }
378     else
379       return BooleanValue.FALSE;
380   }
381
382   /**
383    * Returns the current value of the array.
384    */

385   public static Value shuffle(ArrayValue array)
386   {
387     if (array == null)
388       return BooleanValue.FALSE;
389
390     array.shuffle();
391
392     return BooleanValue.TRUE;
393   }
394
395   /**
396    * Resets the pointer to the end
397    */

398   public static Value end(Value value)
399   {
400     if (value instanceof ArrayValue) {
401       ArrayValue array = (ArrayValue) value;
402
403       return array.end();
404     }
405     else
406       return BooleanValue.FALSE;
407   }
408
409   /**
410    * Checks if the key is in the given array
411    *
412    * @param key a key to check for in the array
413    * @param searchArray the array to search for the key in
414    * @return true if the key is in the array, and false otherwise
415    */

416   public static boolean array_key_exists(Env env,
417                                          @ReadOnly Value key,
418                                          @ReadOnly Value searchArray)
419   {
420     if (! searchArray.isset() || ! key.isset())
421       return false;
422
423     if (!((searchArray instanceof ArrayValue) || (searchArray instanceof ObjectValue))) {
424       env.warning(L.l("'" + searchArray.toString() + "' is an unexpected argument, expected ArrayValue or ObjectValue"));
425       return false;
426     }
427
428     if (!((key instanceof StringValue) || (key instanceof LongValue))) {
429       env.warning(L.l(
430         "The first argument (a '{0}') should be either a string or an integer",
431     key.getType()));
432       return false;
433     }
434     
435     if (searchArray instanceof ArrayValue)
436       return ((ArrayValue) searchArray).containsKey(key) != null;
437     else
438       return ! searchArray.getField(env, key.toString()).isNull();
439   }
440
441   /**
442    * Undocumented alias for {@link #array_key_exists}.
443    */

444   public static boolean key_exists(Env env,
445                                    @ReadOnly Value key,
446                                    @ReadOnly Value searchArray)
447   {
448     return array_key_exists(env, key, searchArray);
449   }
450
451   /**
452    * Returns an array of the keys in the given array
453    *
454    * @param array the array to obtain the keys for
455    * @param searchValue the corresponding value of the returned key array
456    * @return an array containing the keys
457    */

458   public Value array_keys(Env env,
459                           @ReadOnly ArrayValue array,
460                           @Optional @ReadOnly Value searchValue,
461                           @Optional boolean isStrict)
462   {
463     if (array == null)
464       return NullValue.NULL;
465
466     ArrayValue newArray = new ArrayValueImpl(array.getSize());
467
468     for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
469       Value entryValue = entry.getValue();
470       Value entryKey = entry.getKey();
471
472
473       if (searchValue == null || searchValue instanceof DefaultValue)
474         newArray.put(entryKey);
475       else if (entryValue.eq(searchValue))
476         newArray.put(entryKey);
477     }
478
479     return newArray;
480   }
481
482   /**
483    * Returns an array with a number of indices filled with the given value,
484    * starting at the start index.
485    *
486    * @param start the index to start filling the array
487    * @param num the number of entries to fill
488    * @param value the value to fill the entries with
489    * @return an array filled with the given value starting from the given start
490    * index
491    */

492   public Value array_fill(Env env, long start, long num, Value value)
493   {
494
495     if (num < 0) {
496       env.warning("Number of elements must be positive");
497
498       return BooleanValue.FALSE;
499     }
500
501     ArrayValue array = new ArrayValueImpl();
502
503     for (long k = start; k < num + start; k++)
504       array.put(LongValue.create(k), value);
505
506     return array;
507   }
508
509   /**
510    * Returns an array with the given array's keys as values and its values as
511    * keys. If the given array has matching values, the latest value will be
512    * transfered and the others will be lost.
513    *
514    * @param array the array to flip
515    * @return an array with it's keys and values swapped
516    */

517   public Value array_flip(Env env,
518                           ArrayValue array)
519   {
520     if (array == null)
521       return BooleanValue.FALSE;
522
523     ArrayValue newArray = new ArrayValueImpl();
524
525     for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
526       Value entryValue = entry.getValue();
527
528       if ((entryValue instanceof LongValue) ||
529           (entryValue instanceof StringValue))
530         newArray.put(entryValue, entry.getKey());
531       else
532         env.warning("Can only flip STRING and INTEGER values!");
533     }
534
535     return newArray;
536   }
537
538   /**
539    * Returns an array with either the front/end padded with the pad value. If
540    * the pad size is positive, the padding is performed on the end. If
541    * negative, then the array is padded on the front. The pad size is the new
542    * array size. If this size is not greater than the current array size, then
543    * the original input array is returned.
544    *
545    * @param input the array to pad
546    * @param padSize the amount to pad the array by
547    * @param padValue determines front/back padding and the value to place in the
548    * padded space
549    * @return a padded array
550    */

551   public Value array_pad(Env env, ArrayValue input, long padSize,
552                          Value padValue)
553   {
554     if (input == null)
555       return NullValue.NULL;
556
557     long inputSize = input.getSize();
558
559     long size = Math.abs(padSize);
560
561     if (input.getSize() >= size)
562       return input;
563
564     if (size - inputSize > 1048576) {
565       env.warning("You may only pad up to 1048576 elements at a time");
566
567       return BooleanValue.FALSE;
568     }
569
570     ArrayValue paddedArray = new ArrayValueImpl();
571
572     boolean padFront = padSize < 0;
573
574     Iterator JavaDoc<Value> keyIterator = input.keySet().iterator();
575
576     for (long ctr = 0; ctr < size; ctr++) {
577       Value newValue;
578
579       if (padFront && ctr < size - inputSize)
580         newValue = padValue;
581       else if ((! padFront) && ctr >= inputSize)
582         newValue = padValue;
583       else
584         newValue = input.get(keyIterator.next());
585
586       paddedArray.put(LongValue.create(ctr), newValue);
587     }
588
589     return paddedArray;
590   }
591
592   /**
593    * Returns an array that filters out any values that do not hold true when
594    * used in the callback function.
595    *
596    * @param array the array to filter
597    * @param callback the function name for filtering
598    * @return a filtered array
599    */

600   public Value array_filter(Env env,
601                             ArrayValue array,
602                             @Optional Callback callback)
603   {
604     if (array == null)
605       return NullValue.NULL;
606
607     ArrayValue filteredArray = new ArrayValueImpl();
608
609     if (callback != null) {
610
611       if (!callback.isValid()) {
612         env.warning("The second argument, '" + ((CallbackFunction) callback).getFunctionName() + "', should be a valid callback");
613         return NullValue.NULL;
614       }
615
616       for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
617         try {
618           boolean isMatch = callback.call(env, entry.getValue()).toBoolean();
619
620           if (isMatch)
621             filteredArray.put(entry.getKey(), entry.getValue());
622         }
623         catch (Throwable JavaDoc t) {
624           log.log(Level.WARNING, t.toString(), t);
625           env.warning("An error occurred while invoking the filter callback");
626
627           return NullValue.NULL;
628         }
629       }
630     }
631     else {
632
633       for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
634         if (entry.getValue().toBoolean())
635           filteredArray.put(entry.getKey(), entry.getValue());
636       }
637     }
638
639     return filteredArray;
640   }
641
642   /**
643    * Returns the product of the input array's elements as a double.
644    *
645    * @param array the array for who's product is to be found
646    * @return the produce of the array's elements
647    */

648   public Value array_product(Env env,
649                              ArrayValue array)
650   {
651     if (array == null)
652       return NullValue.NULL;
653
654     if (array.getSize() == 0)
655       return DoubleValue.create(0);
656
657     double product = 1;
658
659     for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet())
660       product *= entry.getValue().toDouble();
661
662     return DoubleValue.create(product);
663   }
664
665   /**
666    * Appends a value to the array
667    *
668    * @return the number of elements in the final array
669    */

670   public int array_push(Env env, ArrayValue array, Value []values)
671   {
672     for (Value value : values) {
673       array.put(value);
674     }
675
676     return array.getSize();
677   }
678
679   /**
680    * Returns num sized array of random keys from the given array
681    *
682    * @param array the array from which the keys will come from
683    * @param num the number of random keys to return
684    * @return the produce of the array's elements
685    */

686   public Value array_rand(Env env,
687                           ArrayValue array,
688                           @Optional("1") long num)
689   {
690     if (array == null)
691       return NullValue.NULL;
692
693     if (array.getSize() == 0)
694       return NullValue.NULL;
695
696     if (num < 1 || array.getSize() < num) {
697       env.warning("Second argument has to be between 1 and the number of " +
698                   "elements in the array");
699
700       return NullValue.NULL;
701     }
702
703     long arraySize = array.getSize();
704
705     Value[] keys = new Value[(int) arraySize];
706
707     array.keySet().toArray(keys);
708
709     if (num == 1) {
710       int index = (int) (RandomUtil.getRandomLong() % arraySize);
711
712       if (index < 0)
713         index *= -1;
714
715       return keys[index];
716     }
717
718     int length = keys.length;
719     for (int i = 0; i < length; i++) {
720       int rand = RandomUtil.nextInt(length);
721
722       Value temp = keys[rand];
723       keys[rand] = keys[i];
724       keys[i] = temp;
725     }
726
727     ArrayValue randArray = new ArrayValueImpl();
728
729     for (int i = 0; i < num; i++) {
730       randArray.put(keys[i]);
731     }
732
733     return randArray;
734   }
735
736   /**
737    * Returns the value of the array when its elements have been reduced using
738    * the callback function.
739    *
740    * @param array the array to reduce
741    * @param callback the function to use for reducing the array
742    * @param initialValue used as the element before the first element of the
743    * array for purposes of using the callback function
744    * @return the result from reducing the input array with the callback
745    * function
746    */

747   public Value array_reduce(Env env,
748                             ArrayValue array,
749                             String JavaDoc callback,
750                             @Optional("NULL") Value initialValue)
751   {
752     if (array == null)
753       return NullValue.NULL;
754
755     AbstractFunction func = env.findFunction(callback.intern());
756
757     if (func == null) {
758       env.warning("The second argument, '" + callback +
759                   "', should be a valid callback");
760
761       return NullValue.NULL;
762     }
763
764     Value result = initialValue;
765
766     for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
767       try {
768         result = func.call(env, result, entry.getValue());
769       }
770       catch (Throwable JavaDoc t) {
771         // XXX: may be used for error checking later
772
log.log(Level.WARNING, t.toString(), t);
773         env.warning("An error occurred while invoking the reduction callback");
774
775         return NullValue.NULL;
776       }
777     }
778
779     return result;
780   }
781
782   /**
783    * Returns the inputted array reversed, preserving the keys if keyed is true
784    *
785    * @param inputArray the array to reverse
786    * @param keyed true if the keys are to be preservered
787    * @return the array in reverse
788    */

789   public Value array_reverse(Env env,
790                              ArrayValue inputArray,
791                              @Optional("false") boolean keyed)
792   {
793     if (inputArray == null)
794       return NullValue.NULL;
795
796     Map.Entry JavaDoc<Value, Value>[] entryArray =
797       new Map.Entry JavaDoc[inputArray.getSize()];
798
799     inputArray.entrySet().toArray(entryArray);
800
801     ArrayValue newArray = new ArrayValueImpl();
802
803     int newIndex = 0;
804
805     for (int index = entryArray.length - 1; index > -1; index--) {
806       Value currentKey = entryArray[index].getKey();
807
808       Value currentValue = entryArray[index].getValue();
809
810       if (keyed || (currentKey instanceof StringValue))
811         newArray.put(currentKey, currentValue);
812       else {
813         newArray.put(LongValue.create(newIndex), currentValue);
814
815         newIndex++;
816       }
817     }
818
819     return newArray;
820   }
821
822   /**
823    * Returns the key of the needle being searched for or false if it's not
824    * found
825    *
826    * @param needle the value to search for
827    * @param array the array to search
828    * @param strict checks for type aswell
829    * @return the key of the needle
830    */

831   public Value array_search(Env env,
832                             @ReadOnly Value needle,
833                             @ReadOnly ArrayValue array,
834                             @Optional("false") boolean strict)
835   {
836     if (array == null)
837       return BooleanValue.FALSE;
838
839     for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
840       Value entryValue = entry.getValue();
841
842       Value entryKey = entry.getKey();
843
844       if (needle.eq(entryValue)) {
845         if (strict) {
846           if ((entryValue.getType()).equals(needle.getType()))
847             return entryKey;
848         }
849         else
850           return entryKey;
851       }
852     }
853
854     return BooleanValue.FALSE;
855   }
856
857   /**
858    * Shifts the elements in the array left by one, returning the leftmost value
859    *
860    * @param array the array to shift
861    * @return the left most value in the array
862    */

863   public Value array_shift(Env env,
864                            ArrayValue array)
865   {
866     if (array == null)
867       return NullValue.NULL;
868
869     if (array.getSize() < 1)
870       return NullValue.NULL;
871
872     Iterator JavaDoc<Value> iterator = array.keySet().iterator();
873
874     Value firstValue = array.remove(iterator.next());
875
876     array.keyReset(0, NOT_STRICT);
877
878     return firstValue;
879   }
880
881   /**
882    * Returns a chunk of the array. The offset is the start index, elements is
883    * the number of values to take, and presKeys is if the keys are to be
884    * preserved. If offset is negative, then it's that number from the end of the
885    * array. If elements is negative, then the new array will have from offset
886    * to elements number of values.
887    *
888    * @param array the array to take the chunk from
889    * @param offset the start index for the new array chunk
890    * @param elements the number of elements in the array chunk
891    * @param presKeys true if the keys of the elements are to be preserved, false
892    * otherwise
893    * @return the array chunk
894    */

895   public Value array_slice(Env env,
896                            ArrayValue array,
897                            long offset,
898                            @Optional("NULL") Value elements,
899                            @Optional("false") boolean presKeys)
900   {
901     if (array == null)
902       return NullValue.NULL;
903
904     long size = array.getSize();
905
906     long startIndex = offset;
907
908     if (offset < 0)
909       startIndex = size + offset;
910
911     long endIndex = size;
912
913     if (elements != NullValue.NULL) {
914       endIndex = elements.toLong();
915
916       if (endIndex < 0)
917         endIndex += size;
918       else
919         endIndex += startIndex;
920     }
921
922     Iterator JavaDoc<Map.Entry JavaDoc<Value, Value>> iterator = array.entrySet().iterator();
923
924     ArrayValue slicedArray = new ArrayValueImpl();
925
926     for (int k = 0; k < endIndex && iterator.hasNext(); k++) {
927       Map.Entry JavaDoc<Value, Value> entry = iterator.next();
928
929       if (k >= startIndex) {
930         Value entryKey = entry.getKey();
931
932         Value entryValue = entry.getValue();
933
934         if ((entryKey instanceof StringValue) || presKeys)
935           slicedArray.put(entryKey, entryValue);
936         else
937           slicedArray.put(entryValue);
938       }
939     }
940
941     return slicedArray;
942   }
943
944   /**
945    * Returns the removed chunk of the arrayV and splices in replace. If offset
946    * is negative, then the start index is that far from the end. Otherwise, it
947    * is the start index. If length is not given then from start index to the
948    * end is removed. If length is negative, that is the index to stop removing
949    * elements. Otherwise that is the number of elements to remove. If replace
950    * is given, replace will be inserted into the arrayV at offset.
951    *
952    * @param array the arrayV to splice
953    * @param offset the start index for the new arrayV chunk
954    * @param length the number of elements to remove / stop index
955    * @param replace the elements to add to the arrayV
956    * @return the part of the arrayV removed from input
957    */

958   public Value array_splice(Env env,
959                 @Reference Value arrayVar, //array gets spliced at offset
960
int offset,
961                             @Optional("NULL") Value length,
962                             @Optional Value replace)
963   {
964     if (! arrayVar.isset())
965       return NullValue.NULL;
966
967     ArrayValue array = arrayVar.toArrayValue(env);
968
969     if (array == null)
970       return NullValue.NULL;
971     
972     int size = array.getSize();
973
974     int startIndex = offset;
975
976     if (startIndex < 0)
977       startIndex += size;
978
979     int endIndex = size;
980
981     if (length != NullValue.NULL) {
982       endIndex = length.toInt();
983
984       if (endIndex < 0)
985         endIndex += size;
986       else
987         endIndex += startIndex;
988     }
989
990     return spliceImpl(arrayVar, array, startIndex, endIndex,
991               (ArrayValue) replace.toArray());
992   }
993
994   public Value spliceImpl(Value var,
995               ArrayValue array,
996               int start,
997               int end,
998               ArrayValue replace)
999   {
1000    int index = 0;
1001
1002    ArrayValue newArray = new ArrayValueImpl();
1003    ArrayValue result = new ArrayValueImpl();
1004
1005    var.set(newArray);
1006
1007    ArrayValue.Entry ptr = array.getHead();
1008    for (; ptr != null; ptr = ptr.getNext()) {
1009      Value key = ptr.getKey();
1010      
1011      if (start == index && replace != null) {
1012    for (ArrayValue.Entry replaceEntry = replace.getHead();
1013         replaceEntry != null;
1014         replaceEntry = replaceEntry.getNext()) {
1015      newArray.put(replaceEntry.getValue());
1016    }
1017      }
1018      
1019      if (start <= index && index < end) {
1020    if (ptr.getKey() instanceof StringValue)
1021      result.put(ptr.getKey(), ptr.getValue());
1022    else
1023      result.put(ptr.getValue());
1024      }
1025      else {
1026    if (ptr.getKey() instanceof StringValue)
1027      newArray.put(ptr.getKey(), ptr.getValue());
1028    else
1029      newArray.put(ptr.getValue());
1030      }
1031
1032      index++;
1033    }
1034
1035    if (index <= start && replace != null) {
1036      for (ArrayValue.Entry replaceEntry = replace.getHead();
1037       replaceEntry != null;
1038       replaceEntry = replaceEntry.getNext()) {
1039    newArray.put(replaceEntry.getValue());
1040      }
1041    }
1042
1043    return result;
1044  }
1045
1046  /**
1047   * Returns the sum of the elements in the array
1048   *
1049   * @param array the array to sum
1050   * @return the sum of the elements
1051   */

1052  public Value array_sum(Env env,
1053                         @ReadOnly ArrayValue array)
1054  {
1055    if (array == null)
1056      return NullValue.NULL;
1057
1058    double sum = 0;
1059
1060    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet())
1061      sum += entry.getValue().toDouble();
1062
1063    return DoubleValue.create(sum);
1064  }
1065
1066  // XXX: array_udiff
1067
// XXX: array_udiff_assoc
1068
// XXX: array_udiff_uassoc
1069

1070  // XXX: array_uintersect
1071
// XXX: array_uintersect_assoc
1072
// XXX: array_uintersect_uassoc
1073

1074  /**
1075   * Returns the inputted array without duplicates
1076   *
1077   * @param array the array to get rid of the duplicates from
1078   * @return an array without duplicates
1079   */

1080  public Value array_unique(Env env,
1081                            ArrayValue array)
1082  {
1083    if (array == null)
1084      return BooleanValue.FALSE;
1085
1086    array.sort(CNO_VALUE_NORMAL, NO_KEY_RESET, NOT_STRICT);
1087
1088    Map.Entry JavaDoc<Value, Value> lastEntry = null;
1089
1090    ArrayValue uniqueArray = new ArrayValueImpl();
1091
1092    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
1093      Value entryValue = entry.getValue();
1094
1095      if (lastEntry == null) {
1096        uniqueArray.put(entry.getKey(), entryValue);
1097
1098        lastEntry = entry;
1099
1100        continue;
1101      }
1102
1103      Value lastEntryValue = lastEntry.getValue();
1104
1105      if (! entryValue.toString().equals(lastEntryValue.toString()))
1106        uniqueArray.put(entry.getKey(), entryValue);
1107
1108      lastEntry = entry;
1109    }
1110
1111    uniqueArray.sort(CNO_KEY_NORMAL, NO_KEY_RESET, NOT_STRICT);
1112
1113    return uniqueArray;
1114  }
1115
1116  /**
1117   * Prepends the elements to the array
1118   *
1119   * @param array the array to shift
1120   * @param values
1121   * @return the left most value in the array
1122   */

1123  public Value array_unshift(Env env,
1124                             ArrayValue array,
1125                             Value []values)
1126  {
1127    if (array == null)
1128      return NullValue.NULL;
1129
1130    for (int i = values.length - 1; i >= 0; i--) {
1131      array.unshift(values[i]);
1132    }
1133
1134    array.keyReset(0, NOT_STRICT);
1135
1136    return array;
1137  }
1138
1139  /**
1140   * Returns the values in the passed array with numerical indices.
1141   *
1142   * @param array the array to get the values from
1143   * @return an array with the values of the passed array
1144   */

1145  public Value array_values(Env env,
1146                            ArrayValue array)
1147  {
1148    if (array == null)
1149      return NullValue.NULL;
1150
1151    ArrayValue arrayValues = new ArrayValueImpl();
1152
1153    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet())
1154      arrayValues.put(entry.getValue());
1155
1156    return arrayValues;
1157  }
1158
1159  /**
1160   * Recursively executes a callback function on all elements in the array,
1161   * including elements of elements (i.e., arrays within arrays). Returns true
1162   * if the process succeeded, otherwise false.
1163   *
1164   * @param array the array to walk
1165   * @param call the name of the callback function
1166   * @param extra extra parameter required by the callback function
1167   * @return true if the walk succedded, false otherwise
1168   */

1169  public boolean array_walk_recursive(Env env,
1170                                      ArrayValue array,
1171                                      Value call,
1172                                      @Optional("NULL") Value extra)
1173  {
1174    if (array == null)
1175      return false;
1176
1177    if (! (call instanceof StringValue)) {
1178      env.warning("Wrong syntax for function name");
1179
1180      return false;
1181    }
1182
1183    AbstractFunction callback = env.findFunction(call.toString().intern());
1184
1185    if (callback == null)
1186      return true;
1187
1188    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
1189      Value entryValue = entry.getValue();
1190
1191      if (entryValue instanceof ArrayValue)
1192        array_walk_recursive(env, (ArrayValue) entryValue, call, extra);
1193      else {
1194        try {
1195          arrayWalkImpl(env, entry, extra, callback);
1196        }
1197        catch (Throwable JavaDoc t) {
1198          log.log(Level.WARNING, t.toString(), t);
1199          env.warning("An error occured while invoking the callback");
1200
1201          return false;
1202        }
1203      }
1204    }
1205
1206    return true;
1207  }
1208
1209  /**
1210   * array_walk_recursive helper function.
1211   *
1212   * @param entry the entry to evaluate
1213   * @param callback the callback function
1214   */

1215  private void arrayWalkImpl(Env env, Map.Entry JavaDoc<Value, Value> entry,
1216                             Value extra, AbstractFunction callback)
1217  {
1218    callback.call(env, entry.getValue(), entry.getKey(), extra);
1219  }
1220
1221  /**
1222   * Executes a callback on each of the elements in the array.
1223   *
1224   * @param array the array to walk along
1225   * @param call the name of the callback function
1226   * @param extra extra parameter required by the callback function
1227   * @return true if the walk succedded, false otherwise
1228   */

1229  public boolean array_walk(Env env,
1230                            ArrayValue array,
1231                            Value call,
1232                            @Optional("NULL") Value extra)
1233  {
1234    if (array == null)
1235      return false;
1236
1237    if (! (call instanceof StringValue)) {
1238      env.warning("Wrong syntax for function name");
1239
1240      return false;
1241    }
1242
1243    AbstractFunction callback = env.findFunction(call.toString().intern());
1244
1245    if (callback == null)
1246      return true;
1247
1248    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
1249      try {
1250        arrayWalkImpl(env, entry, extra, callback);
1251      }
1252      catch (Throwable JavaDoc t) {
1253        // XXX: may be used later for error implementation
1254
log.log(Level.WARNING, t.toString(), t);
1255        env.warning("An error occured while invoking the callback");
1256
1257        return false;
1258      }
1259    }
1260
1261    return true;
1262  }
1263
1264  /**
1265   * Sorts the array based on values in reverse order, preserving keys
1266   *
1267   * @param array the array to sort
1268   * @param sortFlag provides optional methods to process the sort
1269   * @return true if the sort works, false otherwise
1270   * @throws ClassCastException if the elements are not mutually comparable
1271   */

1272  public boolean arsort(Env env, ArrayValue array,
1273                        @Optional long sortFlag)
1274  {
1275    if (array == null)
1276      return false;
1277
1278    switch ((int) sortFlag) {
1279    case SORT_STRING:
1280      array.sort(CS_VALUE_REVERSE, NO_KEY_RESET, NOT_STRICT);
1281      break;
1282    case SORT_NUMERIC:
1283      array.sort(CN_VALUE_REVERSE, NO_KEY_RESET, NOT_STRICT);
1284      break;
1285    case SORT_LOCALE_STRING:
1286      Locale JavaDoc locale = env.getLocaleInfo().getCollate();
1287      array.sort(new CompareLocale(ArrayValue.GET_VALUE, SORT_REVERSE,
1288                                   Collator.getInstance(locale)),
1289                 NO_KEY_RESET, NOT_STRICT);
1290      break;
1291    default:
1292      array.sort(CNO_VALUE_REVERSE, NO_KEY_RESET, NOT_STRICT);
1293      break;
1294    }
1295
1296    return true;
1297  }
1298
1299  /**
1300   * Sorts the array based on values in ascending order, preserving keys
1301   *
1302   * @param array the array to sort
1303   * @param sortFlag provides optional methods to process the sort
1304   * @return true if the sort works, false otherwise
1305   * @throws ClassCastException if the elements are not mutually comparable
1306   */

1307  public boolean asort(Env env, ArrayValue array,
1308                       @Optional long sortFlag)
1309  {
1310    if (array == null)
1311      return false;
1312
1313    switch ((int) sortFlag) {
1314    case SORT_STRING:
1315      array.sort(CS_VALUE_NORMAL, NO_KEY_RESET, NOT_STRICT);
1316      break;
1317    case SORT_NUMERIC:
1318      array.sort(CN_VALUE_NORMAL, NO_KEY_RESET, NOT_STRICT);
1319      break;
1320    case SORT_LOCALE_STRING:
1321      Locale JavaDoc locale = env.getLocaleInfo().getCollate();
1322      array.sort(new CompareLocale(ArrayValue.GET_VALUE, SORT_NORMAL,
1323                                   Collator.getInstance(locale)),
1324                 NO_KEY_RESET, NOT_STRICT);
1325      break;
1326    default:
1327      array.sort(CNO_VALUE_NORMAL, NO_KEY_RESET, NOT_STRICT);
1328      break;
1329    }
1330
1331    return true;
1332  }
1333
1334  /**
1335   * Sorts the array based on keys in ascending order, preserving keys
1336   *
1337   * @param array the array to sort
1338   * @param sortFlag provides optional methods to process the sort
1339   * @return true if the sort works, false otherwise
1340   * @throws ClassCastException if the elements are not mutually comparable
1341   */

1342  public boolean ksort(Env env, ArrayValue array,
1343                       @Optional long sortFlag)
1344  {
1345    if (array == null)
1346      return false;
1347
1348    switch ((int) sortFlag) {
1349    case SORT_STRING:
1350      array.sort(CS_KEY_NORMAL, NO_KEY_RESET, NOT_STRICT);
1351      break;
1352    case SORT_NUMERIC:
1353      array.sort(CN_KEY_NORMAL, NO_KEY_RESET, NOT_STRICT);
1354      break;
1355    case SORT_LOCALE_STRING:
1356      Locale JavaDoc locale = env.getLocaleInfo().getCollate();
1357      array.sort(new CompareLocale(ArrayValue.GET_KEY, SORT_NORMAL,
1358                                   Collator.getInstance(locale)),
1359                 NO_KEY_RESET, NOT_STRICT);
1360      break;
1361    default:
1362      array.sort(CNO_KEY_NORMAL, NO_KEY_RESET, NOT_STRICT);
1363      break;
1364    }
1365
1366    return true;
1367  }
1368
1369  /**
1370   * Sorts the array based on keys in reverse order, preserving keys
1371   *
1372   * @param array the array to sort
1373   * @param sortFlag provides optional methods to process the sort
1374   * @return true if the sort works, false otherwise
1375   * @throws ClassCastException if the elements are not mutually comparable
1376   */

1377  public boolean krsort(Env env, ArrayValue array,
1378                        @Optional long sortFlag)
1379  {
1380    if (array == null)
1381      return false;
1382
1383    switch ((int) sortFlag) {
1384    case SORT_STRING:
1385      array.sort(CS_KEY_REVERSE, NO_KEY_RESET, NOT_STRICT);
1386      break;
1387    case SORT_NUMERIC:
1388      array.sort(CN_KEY_REVERSE, NO_KEY_RESET, NOT_STRICT);
1389      break;
1390    case SORT_LOCALE_STRING:
1391      Locale JavaDoc locale = env.getLocaleInfo().getCollate();
1392      array.sort(new CompareLocale(ArrayValue.GET_KEY, SORT_REVERSE,
1393                                   Collator.getInstance(locale)),
1394                 NO_KEY_RESET, NOT_STRICT);
1395      break;
1396    default:
1397      array.sort(CNO_KEY_REVERSE, NO_KEY_RESET, NOT_STRICT);
1398      break;
1399    }
1400
1401    return true;
1402  }
1403
1404  /**
1405   * Sorts the array based on string values using natural order, preserving
1406   * keys, case sensitive
1407   *
1408   * @param array the array to sort
1409   * @return true if the sort works, false otherwise
1410   * @throws ClassCastException if the elements are not mutually comparable
1411   */

1412  public Value natsort(ArrayValue array)
1413  {
1414    if (array == null)
1415      return NullValue.NULL;
1416
1417    trimArrayStrings(array);
1418
1419    array.sort(CNA_VALUE_NORMAL_SENSITIVE, NO_KEY_RESET, NOT_STRICT);
1420
1421    return BooleanValue.TRUE;
1422  }
1423
1424  /**
1425   * Sorts the array based on string values using natural order, preserving
1426   * keys, case insensitive
1427   *
1428   * @param array the array to sort
1429   * @return true if the sort works, false otherwise
1430   * @throws ClassCastException if the elements are not mutually comparable
1431   */

1432  public Value natcasesort(ArrayValue array)
1433  {
1434    if (array == null)
1435      return NullValue.NULL;
1436
1437    trimArrayStrings(array);
1438
1439    array.sort(CNA_VALUE_NORMAL_INSENSITIVE, NO_KEY_RESET, NOT_STRICT);
1440
1441    return BooleanValue.TRUE;
1442  }
1443
1444  /**
1445   * Helper function for natsort and natcasesort to trim the string in the
1446   * array
1447   *
1448   * @param array the array to trim strings from
1449   */

1450  private void trimArrayStrings(ArrayValue array)
1451  {
1452    if (array != null) {
1453
1454      for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
1455        Value entryValue = entry.getValue();
1456
1457        if (entryValue instanceof StringValue)
1458          array.put(entry.getKey(),
1459                    StringValue.create(entryValue.toString().trim()));
1460      }
1461    }
1462  }
1463
1464  // XXX: compact
1465

1466  /**
1467   * Determines if the key is in the array
1468   *
1469   * @param needle the array to sort
1470   * @return true if the sort works, false otherwise
1471   * @throws ClassCastException if the elements are not mutually comparable
1472   */

1473  public boolean in_array(@ReadOnly Value needle,
1474                          @ReadOnly ArrayValue stack,
1475                          @Optional("false") boolean strict)
1476  {
1477    if (stack == null)
1478      return false;
1479
1480    if (strict)
1481      return stack.containsStrict(needle) != NullValue.NULL;
1482    else
1483      return stack.contains(needle) != NullValue.NULL;
1484  }
1485
1486  /**
1487   * Sorts the array based on values in ascending order
1488   *
1489   * @param array the array to sort
1490   * @param sortFlag provides optional methods to process the sort
1491   * @return true if the sort works, false otherwise
1492   * @throws ClassCastException if the elements are not mutually comparable
1493   */

1494  public boolean sort(Env env, ArrayValue array, @Optional long sortFlag)
1495  {
1496    if (array == null)
1497      return false;
1498
1499    switch ((int) sortFlag) {
1500    case SORT_STRING:
1501      array.sort(CS_VALUE_NORMAL, KEY_RESET, STRICT);
1502      break;
1503    case SORT_NUMERIC:
1504      array.sort(CN_VALUE_NORMAL, KEY_RESET, STRICT);
1505      break;
1506    case SORT_LOCALE_STRING:
1507      Locale JavaDoc locale = env.getLocaleInfo().getCollate();
1508      array.sort(new CompareLocale(ArrayValue.GET_VALUE, SORT_NORMAL,
1509                                   Collator.getInstance(locale)),
1510                 KEY_RESET, STRICT);
1511      break;
1512    default:
1513      array.sort(CNO_VALUE_NORMAL, KEY_RESET, STRICT);
1514      break;
1515    }
1516
1517    return true;
1518  }
1519
1520  /**
1521   * Sorts the array based on values in reverse order
1522   *
1523   * @param array the array to sort
1524   * @param sortFlag provides optional methods to process the sort
1525   * @return true if the sort works, false otherwise
1526   * @throws ClassCastException if the elements are not mutually comparable
1527   */

1528  public boolean rsort(Env env, ArrayValue array, @Optional long sortFlag)
1529  {
1530    if (array == null)
1531      return false;
1532
1533    switch ((int) sortFlag) {
1534    case SORT_STRING:
1535      array.sort(CS_VALUE_REVERSE, KEY_RESET, STRICT);
1536      break;
1537    case SORT_NUMERIC:
1538      array.sort(CN_VALUE_REVERSE, KEY_RESET, STRICT);
1539      break;
1540    case SORT_LOCALE_STRING:
1541      Locale JavaDoc locale = env.getLocaleInfo().getCollate();
1542      array.sort(new CompareLocale(ArrayValue.GET_VALUE, SORT_REVERSE,
1543                                   Collator.getInstance(locale)),
1544                 KEY_RESET, STRICT);
1545      break;
1546    default:
1547      array.sort(CNO_VALUE_REVERSE, KEY_RESET, STRICT);
1548      break;
1549    }
1550
1551    return true;
1552  }
1553
1554  /**
1555   * Sorts the array based on values in ascending order using a callback
1556   * function
1557   *
1558   * @param array the array to sort
1559   * @param func the name of the callback function
1560   * @param sortFlag provides optional methods to process the sort
1561   * @return true if the sort works, false otherwise
1562   * @throws ClassCastException if the elements are not mutually comparable
1563   */

1564  public boolean usort(Env env,
1565                       ArrayValue array,
1566                       Callback func,
1567                       @Optional long sortFlag)
1568  {
1569    if (array == null)
1570      return false;
1571
1572    if (!func.isValid()) {
1573      env.warning(L.l("Invalid comparison function"));
1574      return false;
1575    }
1576
1577    CompareCallBack cmp;
1578
1579    cmp = new CompareCallBack(ArrayValue.GET_VALUE, SORT_NORMAL, func, env);
1580
1581    array.sort(cmp, KEY_RESET, STRICT);
1582
1583    return true;
1584  }
1585
1586  /**
1587   * Sorts the array based on values in ascending order using a callback
1588   * function
1589   *
1590   * @param array the array to sort
1591   * @param func the name of the callback function
1592   * @param sortFlag provides optional methods to process the sort
1593   * @return true if the sort works, false otherwise
1594   * @throws ClassCastException if the elements are not mutually comparable
1595   */

1596  public boolean uasort(Env env,
1597                        ArrayValue array,
1598                        Callback func,
1599                        @Optional long sortFlag)
1600  {
1601    if (array == null)
1602      return false;
1603
1604    if (!func.isValid()) {
1605      env.warning(L.l("Invalid comparison function"));
1606      return false;
1607    }
1608
1609    array.sort(new CompareCallBack(ArrayValue.GET_VALUE, SORT_NORMAL, func,
1610                                   env), NO_KEY_RESET, NOT_STRICT);
1611
1612    return true;
1613  }
1614
1615  /**
1616   * Sorts the array based on values in ascending order using a callback
1617   * function
1618   *
1619   * @param array the array to sort
1620   * @param func the name of the callback function
1621   * @param sortFlag provides optional methods to process the sort
1622   * @return true if the sort works, false otherwise
1623   * @throws ClassCastException if the elements are not mutually comparable
1624   */

1625  public boolean uksort(Env env,
1626                        ArrayValue array,
1627                        Callback func,
1628                        @Optional long sortFlag)
1629  {
1630    if (array == null)
1631      return false;
1632
1633    if (!func.isValid()) {
1634      env.warning(L.l("Invalid comparison function"));
1635      return false;
1636    }
1637
1638    CompareCallBack cmp;
1639
1640    cmp = new CompareCallBack(ArrayValue.GET_KEY, SORT_NORMAL, func, env);
1641
1642    array.sort(cmp, NO_KEY_RESET, NOT_STRICT);
1643
1644    return true;
1645  }
1646
1647  /**
1648   * Creates an array using the start and end values provided
1649   *
1650   * @param start the 0 index element
1651   * @param end the length - 1 index element
1652   * @param step the new value is increased by this to determine the value for
1653   * the next element
1654   * @return the new array
1655   */

1656  public Value range(Env env,
1657                     @ReadOnly Value start,
1658                     @ReadOnly Value end,
1659                     @Optional("1") long step)
1660  {
1661    if (step < 1)
1662      step = 1;
1663
1664    if (!start.getType().equals(end.getType())) {
1665      start = LongValue.create(start.toLong());
1666      end = LongValue.create(end.toLong());
1667    }
1668    else if (Character.isDigit(start.toChar())) {
1669      start = LongValue.create(start.toLong());
1670      end = LongValue.create(end.toLong());
1671    }
1672    else {
1673      start = rangeIncrement(start, 0);
1674      end = rangeIncrement(end, 0);
1675    }
1676
1677    if (start.eq(end)) {
1678    }
1679    else if (start instanceof StringValue &&
1680             (Math.abs(end.toChar() - start.toChar()) < step)) {
1681      env.warning("steps exceeds the specified range");
1682
1683      return BooleanValue.FALSE;
1684    }
1685    else if (start instanceof LongValue &&
1686             (Math.abs(end.toLong() - start.toLong()) < step)) {
1687      env.warning("steps exceeds the specified range");
1688
1689      return BooleanValue.FALSE;
1690    }
1691
1692    boolean increment = true;
1693
1694    if (! end.geq(start)) {
1695      step *= -1;
1696      increment = false;
1697    }
1698
1699    ArrayValue array = new ArrayValueImpl();
1700
1701    do {
1702      array.put(start);
1703
1704      start = rangeIncrement(start, step);
1705    } while ((increment && start.leq(end)) ||
1706             (!increment && start.geq(end)));
1707
1708    return array;
1709  }
1710
1711  private Value rangeIncrement(Value value, long step)
1712  {
1713    if (value instanceof StringValue)
1714      return StringValue.create((char) (value.toChar() + step));
1715
1716    return LongValue.create(value.toLong() + step);
1717  }
1718
1719  // XXX:You'll need to mark the function as XXX:, because I need to add an
1720
// attribute like @ModifiedSymbolTable and change some analysis of the
1721
// compilation based on that attribute.
1722
//
1723
// Basically, the compiled mode uses Java variables to store PHP
1724
// variables. The extract() call messes that up, or at least forces the
1725
// compiler to synchronize its view of the variables.
1726
// (email Re:extract: symbol table)
1727

1728  /**
1729   * Inputs new variables into the symbol table from the passed array
1730   *
1731   * @param array the array contained the new variables
1732   * @param rawType flag to determine how to handle collisions
1733   * @param valuePrefix used along with the flag
1734   * @return the number of new variables added from the array to the symbol
1735   * table
1736   */

1737  @UsesSymbolTable
1738  public static Value extract(Env env,
1739                              ArrayValue array,
1740                              @Optional("EXTR_OVERWRITE") long rawType,
1741                              @Optional("NULL") Value valuePrefix)
1742  {
1743    if (array == null)
1744      return NullValue.NULL;
1745
1746    long extractType = rawType & ~EXTR_REFS;
1747
1748    boolean extrRefs = (rawType & EXTR_REFS) != 0;
1749
1750    if (extractType < EXTR_OVERWRITE || extractType > EXTR_IF_EXISTS &&
1751                                        extractType != EXTR_REFS) {
1752      env.warning("Unknown extract type");
1753
1754      return NullValue.NULL;
1755    }
1756
1757    if (extractType >= EXTR_PREFIX_SAME &&
1758        extractType <= EXTR_PREFIX_IF_EXISTS &&
1759        (valuePrefix == null || (! (valuePrefix instanceof StringValue)))) {
1760      env.warning("Prefix expected to be specified");
1761
1762      return NullValue.NULL;
1763    }
1764
1765    String JavaDoc prefix = "";
1766
1767    if (valuePrefix instanceof StringValue)
1768      prefix = valuePrefix.toString() + "_";
1769
1770    int completedSymbols = 0;
1771
1772    for (Value entryKey : array.keySet()) {
1773      Value entryValue;
1774
1775      if (extrRefs)
1776        entryValue = array.getRef(entryKey);
1777      else
1778        entryValue = array.get(entryKey);
1779
1780      String JavaDoc symbolName = entryKey.toString();
1781
1782      Value tableValue = env.getValue(symbolName);
1783
1784      switch ((int) extractType) {
1785      case EXTR_SKIP:
1786        if (tableValue != NullValue.NULL)
1787          symbolName = "";
1788
1789        break;
1790      case EXTR_PREFIX_SAME:
1791        if (tableValue != NullValue.NULL)
1792          symbolName = prefix + symbolName;
1793
1794        break;
1795      case EXTR_PREFIX_ALL:
1796        symbolName = prefix + symbolName;
1797
1798        break;
1799      case EXTR_PREFIX_INVALID:
1800        if (! validVariableName(symbolName))
1801          symbolName = prefix + symbolName;
1802
1803        break;
1804      case EXTR_IF_EXISTS:
1805        if (tableValue == NullValue.NULL)
1806          symbolName = "";//entryValue = tableValue;
1807

1808        break;
1809      case EXTR_PREFIX_IF_EXISTS:
1810        if (tableValue != NullValue.NULL)
1811          symbolName = prefix + symbolName;
1812        else
1813          symbolName = "";
1814
1815        break;
1816      default:
1817
1818        break;
1819      }
1820
1821      if (validVariableName(symbolName)) {
1822        env.setValue(symbolName, entryValue);
1823
1824        completedSymbols++;
1825      }
1826    }
1827
1828    return LongValue.create(completedSymbols);
1829  }
1830
1831  /**
1832   * Helper function for extract to determine if a variable name is valid
1833   *
1834   * @param variableName the name to check
1835   * @return true if the name is valid, false otherwise
1836   */

1837  private static boolean validVariableName(String JavaDoc variableName)
1838  {
1839    if (variableName.length() < 1)
1840      return false;
1841
1842    char checkChar = variableName.charAt(0);
1843
1844    if (! Character.isLetter(checkChar) && checkChar != '_')
1845      return false;
1846
1847    for (int k = 1; k < variableName.length(); k++) {
1848      checkChar = variableName.charAt(k);
1849
1850      if (!Character.isLetterOrDigit(checkChar) && checkChar != '_')
1851        return false;
1852    }
1853
1854    return true;
1855  }
1856
1857  /**
1858   * Returns an array with everything that is in array and not in the other
1859   * arrays using a passed callback function for comparing
1860   *
1861   * @param array the primary array
1862   * @param arrays the vector of arrays to check the primary array's values
1863   * against
1864   * @return an array with all of the values that are in the primary array but
1865   * not in the other arrays
1866   */

1867  public Value array_diff(Env env, ArrayValue array, Value []arrays)
1868  {
1869    if (array == null)
1870      return NullValue.NULL;
1871
1872    if (arrays.length < 1) {
1873      env.warning("Wrong parameter count for array_diff()");
1874
1875      return NullValue.NULL;
1876    }
1877
1878    ArrayValue diffArray = new ArrayValueImpl();
1879
1880    boolean valueFound;
1881
1882    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
1883      valueFound = false;
1884
1885      Value entryValue = entry.getValue();
1886
1887      for (int k = 0; k < arrays.length && ! valueFound; k++) {
1888        if (! (arrays[k] instanceof ArrayValue)) {
1889          env.warning("Argument #" + (k + 2) + " is not an array");
1890
1891          return NullValue.NULL;
1892        }
1893
1894        valueFound =
1895          ((ArrayValue) arrays[k]).contains(entryValue) != NullValue.NULL;
1896      }
1897
1898      if (! valueFound)
1899        diffArray.put(entry.getKey(), entryValue);
1900    }
1901
1902    return diffArray;
1903  }
1904
1905
1906  /**
1907   * Returns an array with everything that is in array and not in the other
1908   * arrays, keys also used
1909   *
1910   * @param array the primary array
1911   * @param arrays the vector of arrays to check the primary array's values
1912   * against
1913   * @return an array with all of the values that are in the primary array but
1914   * not in the other arrays
1915   */

1916  public Value array_diff_assoc(Env env, ArrayValue array, Value []arrays)
1917  {
1918    if (array == null)
1919      return NullValue.NULL;
1920
1921    if (arrays.length < 1) {
1922      env.warning("Wrong parameter count for array_diff()");
1923
1924      return NullValue.NULL;
1925    }
1926
1927    ArrayValue diffArray = new ArrayValueImpl();
1928
1929    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
1930      boolean valueFound = false;
1931
1932      Value entryValue = entry.getValue();
1933
1934      Value entryKey = entry.getKey();
1935
1936      for (int k = 0; k < arrays.length && ! valueFound; k++) {
1937        if (! (arrays[k] instanceof ArrayValue)) {
1938          env.warning("Argument #" + (k + 2) + " is not an array");
1939
1940          return NullValue.NULL;
1941        }
1942
1943        valueFound =
1944          ((ArrayValue) arrays[k]).contains(entryValue).eq(entryKey);
1945      }
1946
1947      if (! valueFound)
1948        diffArray.put(entryKey, entryValue);
1949    }
1950
1951    return diffArray;
1952  }
1953
1954  /**
1955   * Returns an array with everything that is in array and not in the other
1956   * arrays, keys used for comparison
1957   *
1958   * @param array the primary array
1959   * @param arrays the vector of arrays to check the primary array's values
1960   * against
1961   * @return an array with all of the values that are in the primary array but
1962   * not in the other arrays
1963   */

1964  public Value array_diff_key(Env env, ArrayValue array, Value []arrays)
1965  {
1966    if (array == null)
1967      return NullValue.NULL;
1968
1969    if (arrays.length < 1) {
1970      env.warning("Wrong parameter count for array_diff()");
1971
1972      return NullValue.NULL;
1973    }
1974
1975    ArrayValue diffArray = new ArrayValueImpl();
1976
1977    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
1978      boolean keyFound = false;
1979
1980      Value entryKey = entry.getKey();
1981
1982      for (int k = 0; k < arrays.length && ! keyFound; k++) {
1983        if (! (arrays[k] instanceof ArrayValue)) {
1984          env.warning("Argument #" + (k + 2) + " is not an array");
1985
1986          return NullValue.NULL;
1987        }
1988
1989        keyFound = ((ArrayValue) arrays[k]).containsKey(entryKey) != null;
1990      }
1991
1992      if (! keyFound)
1993        diffArray.put(entryKey, entry.getValue());
1994    }
1995
1996    return diffArray;
1997  }
1998
1999  /**
2000   * Returns an array with everything that is in array and not in the other
2001   * arrays, keys used for comparison aswell
2002   *
2003   * @param array the primary array
2004   * @param arrays the vector of arrays to check the primary array's values
2005   * against. The last element is the callback function.
2006   * @return an array with all of the values that are in the primary array but
2007   * not in the other arrays
2008   */

2009  public Value array_diff_uassoc(Env env, ArrayValue array, Value []arrays)
2010  {
2011    if (array == null)
2012      return NullValue.NULL;
2013
2014    if (arrays.length < 2) {
2015      env.warning("Wrong parameter count for array_diff()");
2016
2017      return NullValue.NULL;
2018    }
2019
2020    AbstractFunction func =
2021      env.findFunction(arrays[arrays.length - 1].toString().intern());
2022
2023    if (func == null) {
2024      env.warning("Invalid comparison function");
2025
2026      return NullValue.NULL;
2027    }
2028
2029    ArrayValue diffArray = new ArrayValueImpl();
2030
2031    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2032      boolean ValueFound = false;
2033
2034      Value entryValue = entry.getValue();
2035
2036      Value entryKey = entry.getKey();
2037
2038      for (int k = 0; k < arrays.length - 1 && ! ValueFound; k++) {
2039        if (! (arrays[k] instanceof ArrayValue)) {
2040          env.warning("Argument #" + (k + 2) + " is not an array");
2041
2042          return NullValue.NULL;
2043        }
2044
2045        Value searchKey = ((ArrayValue) arrays[k]).contains(entryValue);
2046
2047        if (searchKey != NullValue.NULL)
2048          ValueFound = ((int) func.call(env, searchKey, entryKey).toLong()) ==
2049                       0;
2050      }
2051
2052      if (! ValueFound)
2053        diffArray.put(entryKey, entryValue);
2054    }
2055
2056    return diffArray;
2057  }
2058
2059  /**
2060   * Returns an array with everything that is in array and not in the other
2061   * arrays, keys used for comparison only
2062   *
2063   * @param array the primary array
2064   * @param arrays the vector of arrays to check the primary array's values
2065   * against. The last element is the callback function.
2066   * @return an array with all of the values that are in the primary array but
2067   * not in the other arrays
2068   */

2069  public Value array_diff_ukey(Env env, ArrayValue array, Value []arrays)
2070  {
2071    if (array == null)
2072      return NullValue.NULL;
2073
2074    if (arrays.length < 2) {
2075      env.warning("Wrong parameter count for array_diff()");
2076
2077      return NullValue.NULL;
2078    }
2079
2080    AbstractFunction func =
2081      env.findFunction(arrays[arrays.length - 1].toString().intern());
2082
2083    if (func == null) {
2084      env.warning("Invalid comparison function");
2085
2086      return NullValue.NULL;
2087    }
2088
2089    ArrayValue diffArray = new ArrayValueImpl();
2090
2091    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2092      boolean keyFound = false;
2093
2094      Value entryKey = entry.getKey();
2095
2096      for (int k = 0; k < arrays.length - 1 && ! keyFound; k++) {
2097        if (! (arrays[k] instanceof ArrayValue)) {
2098          env.warning("Argument #" + (k + 2) + " is not an array");
2099
2100          return NullValue.NULL;
2101        }
2102
2103        Iterator JavaDoc<Value> keyItr = ((ArrayValue) arrays[k]).keySet().iterator();
2104
2105        keyFound = false;
2106
2107        while (keyItr.hasNext() && ! keyFound) {
2108          Value currentKey = keyItr.next();
2109
2110          keyFound = ((int) func.call(env, entryKey, currentKey).toLong()) == 0;
2111        }
2112      }
2113
2114      if (! keyFound)
2115        diffArray.put(entryKey, entry.getValue());
2116    }
2117
2118    return diffArray;
2119  }
2120
2121  /**
2122   * Returns an array with everything that is in array and also in the other
2123   * arrays
2124   *
2125   * @param array the primary array
2126   * @param arrays the vector of arrays to check the primary array's values
2127   * against. The last element is the callback function.
2128   * @return an array with all of the values that are in the primary array and
2129   * in the other arrays
2130   */

2131  public Value array_intersect(Env env, ArrayValue array, Value []arrays)
2132  {
2133    if (array == null)
2134      return NullValue.NULL;
2135
2136    if (arrays.length < 1) {
2137      env.warning("Wrong parameter count for array_diff()");
2138
2139      return NullValue.NULL;
2140    }
2141
2142    ArrayValue interArray = new ArrayValueImpl();
2143
2144    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2145      boolean valueFound = false;
2146
2147      Value entryValue = entry.getValue();
2148
2149      for (int k = 0; k < arrays.length; k++) {
2150        if (! (arrays[k] instanceof ArrayValue)) {
2151          env.warning("Argument #" + (k + 2) + " is not an array");
2152
2153          return NullValue.NULL;
2154        }
2155
2156        if (k > 0 && ! valueFound)
2157          break;
2158
2159        valueFound =
2160          ((ArrayValue) arrays[k]).contains(entryValue) != NullValue.NULL;
2161      }
2162
2163      if (valueFound)
2164        interArray.put(entry.getKey(), entryValue);
2165    }
2166
2167    return interArray;
2168  }
2169
2170  /**
2171   * Returns an array with everything that is in array and also in the other
2172   * arrays, keys are also used in the comparison
2173   *
2174   * @param array the primary array
2175   * @param arrays the vector of arrays to check the primary array's values
2176   * against. The last element is the callback function.
2177   * @return an array with all of the values that are in the primary array and
2178   * in the other arrays
2179   */

2180  public Value array_intersect_assoc(Env env, ArrayValue array, Value []arrays)
2181  {
2182    if (array == null)
2183      return NullValue.NULL;
2184
2185    if (arrays.length < 1) {
2186      env.warning("Wrong parameter count for array_diff()");
2187
2188      return NullValue.NULL;
2189    }
2190
2191    ArrayValue interArray = new ArrayValueImpl();
2192
2193    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2194      boolean valueFound = false;
2195
2196      Value entryKey = entry.getKey();
2197
2198      Value entryValue = entry.getValue();
2199
2200      for (int k = 0; k < arrays.length; k++) {
2201        if (! (arrays[k] instanceof ArrayValue)) {
2202          env.warning("Argument #" + (k + 2) + " is not an array");
2203
2204          return NullValue.NULL;
2205        }
2206
2207        if (k > 0 && ! valueFound)
2208          break;
2209
2210        Value searchValue = ((ArrayValue) arrays[k]).containsKey(entryKey);
2211
2212        if (searchValue != null)
2213          valueFound = searchValue.eq(entryValue);
2214        else
2215          valueFound = false;
2216      }
2217
2218      if (valueFound)
2219        interArray.put(entryKey, entryValue);
2220    }
2221
2222    return interArray;
2223  }
2224
2225  /**
2226   * Returns an array with everything that is in array and also in the other
2227   * arrays, keys are only used in the comparison
2228   *
2229   * @param array the primary array
2230   * @param arrays the vector of arrays to check the primary array's values
2231   * against. The last element is the callback function.
2232   * @return an array with all of the values that are in the primary array and
2233   * in the other arrays
2234   */

2235  public Value array_intersect_key(Env env, ArrayValue array, Value []arrays)
2236  {
2237    if (array == null)
2238      return NullValue.NULL;
2239
2240    if (arrays.length < 1) {
2241      env.warning("Wrong parameter count for array_diff()");
2242
2243      return NullValue.NULL;
2244    }
2245
2246    ArrayValue interArray = new ArrayValueImpl();
2247
2248    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2249      boolean keyFound = false;
2250
2251      Value entryKey = entry.getKey();
2252
2253      for (int k = 0; k < arrays.length; k++) {
2254        if (! (arrays[k] instanceof ArrayValue)) {
2255          env.warning("Argument #" + (k + 2) + " is not an array");
2256
2257          return NullValue.NULL;
2258        }
2259
2260        if (k > 0 && ! keyFound)
2261          break;
2262
2263        keyFound = ((ArrayValue) arrays[k]).containsKey(entryKey) != null;
2264      }
2265
2266      if (keyFound)
2267        interArray.put(entryKey, entry.getValue());
2268    }
2269
2270    return interArray;
2271  }
2272
2273  /**
2274   * Returns an array with everything that is in array and also in the other
2275   * arrays, keys are also used in the comparison. Uses a callback function for
2276   * evalutation the keys.
2277   *
2278   * @param array the primary array
2279   * @param arrays the vector of arrays to check the primary array's values
2280   * against. The last element is the callback function.
2281   * @return an array with all of the values that are in the primary array and
2282   * in the other arrays
2283   */

2284  public Value array_intersect_uassoc(Env env, ArrayValue array, Value []arrays)
2285  {
2286    if (array == null)
2287      return NullValue.NULL;
2288
2289    if (arrays.length < 2) {
2290      env.warning("Wrong parameter count for array_diff()");
2291
2292      return NullValue.NULL;
2293    }
2294
2295    AbstractFunction func =
2296      env.findFunction(arrays[arrays.length - 1].toString().intern());
2297
2298    if (func == null) {
2299      env.warning("Invalid comparison function");
2300
2301      return NullValue.NULL;
2302    }
2303
2304    ArrayValue interArray = new ArrayValueImpl();
2305
2306    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2307      boolean valueFound = false;
2308
2309      Value entryKey = entry.getKey();
2310
2311      Value entryValue = entry.getValue();
2312
2313      for (int k = 0; k < arrays.length - 1; k++) {
2314        if (! (arrays[k] instanceof ArrayValue)) {
2315          env.warning("Argument #" + (k + 2) + " is not an array");
2316
2317          return NullValue.NULL;
2318        }
2319
2320        if (k > 0 && ! valueFound)
2321          break;
2322
2323        Value searchValue = ((ArrayValue) arrays[k]).containsKey(entryKey);
2324
2325        if (searchValue != null)
2326          valueFound = func.call(env, searchValue, entryValue).toLong() == 0;
2327        else
2328          valueFound = false;
2329      }
2330
2331      if (valueFound)
2332        interArray.put(entryKey, entryValue);
2333    }
2334
2335    return interArray;
2336  }
2337
2338  /**
2339   * Returns an array with everything that is in array and also in the other
2340   * arrays, keys are only used in the comparison. Uses a callback function for
2341   * evalutation the keys.
2342   *
2343   * @param array the primary array
2344   * @param arrays the vector of arrays to check the primary array's values
2345   * against. The last element is the callback function.
2346   * @return an array with all of the values that are in the primary array and
2347   * in the other arrays
2348   */

2349  public Value array_intersect_ukey(Env env, ArrayValue array, Value []arrays)
2350  {
2351    if (array == null)
2352      return NullValue.NULL;
2353
2354    if (arrays.length < 2) {
2355      env.warning("Wrong parameter count for array_diff()");
2356
2357      return NullValue.NULL;
2358    }
2359
2360    AbstractFunction func =
2361      env.findFunction(arrays[arrays.length - 1].toString().intern());
2362
2363    if (func == null) {
2364      env.warning("Invalid comparison function");
2365
2366      return NullValue.NULL;
2367    }
2368
2369    ArrayValue interArray = new ArrayValueImpl();
2370
2371    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2372      boolean keyFound = false;
2373
2374      Value entryKey = entry.getKey();
2375
2376      for (int k = 0; k < arrays.length - 1; k++) {
2377        if (! (arrays[k] instanceof ArrayValue)) {
2378          env.warning("Argument #" + (k + 2) + " is not an array");
2379
2380          return NullValue.NULL;
2381        }
2382
2383        if (k > 0 && ! keyFound)
2384          break;
2385
2386        Iterator JavaDoc<Value> keyItr = ((ArrayValue) arrays[k]).keySet().iterator();
2387
2388        keyFound = false;
2389
2390        while (keyItr.hasNext() && ! keyFound) {
2391          Value currentKey = keyItr.next();
2392
2393          keyFound = ((int) func.call(env, entryKey, currentKey).toLong()) == 0;
2394        }
2395
2396      }
2397
2398      if (keyFound)
2399        interArray.put(entryKey, entry.getValue());
2400    }
2401
2402    return interArray;
2403  }
2404
2405  /**
2406   * Maps the given function with the array arguments.
2407   *
2408   * @param fun the function name
2409   * @param args the vector of array arguments
2410   * @return an array with all of the mapped values
2411   */

2412  public Value array_map(Env env, Callback fun,
2413                         ArrayValue arg, Value []args)
2414  {
2415    // quercus/1730
2416
Iterator JavaDoc<Map.Entry JavaDoc<Value, Value>> argIter = arg.entrySet().iterator();
2417
2418    Iterator JavaDoc []iters = new Iterator JavaDoc[args.length];
2419    for (int i = 0; i < args.length; i++) {
2420      if (! (args[i] instanceof ArrayValue))
2421        throw env.errorException(L.l("expected array"));
2422
2423      ArrayValue argArray = (ArrayValue) args[i];
2424
2425      iters[i] = argArray.values().iterator();
2426    }
2427
2428    ArrayValue resultArray = new ArrayValueImpl();
2429
2430    Value []param = new Value[args.length + 1];
2431    while (argIter.hasNext()) {
2432      Map.Entry JavaDoc<Value, Value> entry = argIter.next();
2433
2434      param[0] = entry.getValue();
2435
2436      for (int i = 0; i < iters.length; i++) {
2437        param[i + 1] = (Value) iters[i].next();
2438
2439        if (param[i + 1] == null)
2440          param[i + 1] = NullValue.NULL;
2441      }
2442
2443      resultArray.put(entry.getKey(), fun.call(env, param));
2444    }
2445
2446    return resultArray;
2447  }
2448
2449  /**
2450   * Maps the given function with the array arguments.
2451   *
2452   * @param args the vector of array arguments
2453   * @return an array with all of the mapped values
2454   */

2455  public Value array_merge(Value []args)
2456  {
2457    // quercus/1731
2458

2459    ArrayValue result = new ArrayValueImpl();
2460
2461    for (Value arg : args) {
2462      if (arg.isNull())
2463    return NullValue.NULL;
2464      
2465      if (! (arg.toValue() instanceof ArrayValue))
2466        continue;
2467
2468      ArrayValue array = (ArrayValue) arg.toValue();
2469
2470      for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2471        Value key = entry.getKey();
2472        Value value = entry.getValue();
2473
2474        if (key.isNumberConvertible())
2475          result.put(value);
2476        else
2477          result.put(key, value);
2478      }
2479    }
2480
2481    return result;
2482  }
2483
2484  /**
2485   * Maps the given function with the array arguments.
2486   *
2487   * @param args the vector of array arguments
2488   * @return an array with all of the mapped values
2489   */

2490  public Value array_merge_recursive(Value []args)
2491  {
2492    // quercus/173a
2493

2494    ArrayValue result = new ArrayValueImpl();
2495
2496    for (Value arg : args) {
2497      if (! (arg.toValue() instanceof ArrayValue))
2498        continue;
2499
2500      arrayMergeRecursiveImpl(result, (ArrayValue) arg.toValue());
2501    }
2502
2503    return result;
2504  }
2505
2506  private static void arrayMergeRecursiveImpl(ArrayValue result,
2507                                              ArrayValue array)
2508  {
2509    for (Map.Entry JavaDoc<Value, Value> entry : array.entrySet()) {
2510      Value key = entry.getKey();
2511      Value value = entry.getValue().toValue();
2512
2513      if (key.isNumberConvertible()) {
2514        result.put(value);
2515      }
2516      else {
2517        Value oldValue = result.get(key).toValue();
2518
2519        if (oldValue != null && oldValue.isset()) {
2520          if (oldValue.isArray() && value.isArray()) {
2521            arrayMergeRecursiveImpl((ArrayValue) oldValue, (ArrayValue) value);
2522          }
2523          else if (oldValue.isArray()) {
2524            oldValue.put(value);
2525          }
2526          else if (value.isArray()) {
2527            // XXX: s/b insert?
2528
value.put(oldValue);
2529          }
2530          else {
2531            ArrayValue newArray = new ArrayValueImpl();
2532
2533            newArray.put(oldValue);
2534            newArray.put(value);
2535
2536            result.put(key, newArray);
2537          }
2538        }
2539        else {
2540          result.put(key, value);
2541        }
2542      }
2543    }
2544  }
2545
2546  /**
2547   * Sort the arrays like rows in a database.
2548   * @param arrays arrays to sort
2549   *
2550   * @return true on success, and false on failure
2551   */

2552  public boolean array_multisort(Env env, Value[] arrays)
2553  {
2554    int maxsize = 0;
2555    for(int i=0; i<arrays.length; i++)
2556      if (arrays[i] instanceof ArrayValue)
2557    maxsize = Math.max(maxsize,
2558               ((ArrayValue)arrays[i]).getSize());
2559
2560    // create the identity permutation [1..n]
2561
LongValue []rows = new LongValue[maxsize];
2562    for(int i=0; i<rows.length; i++)
2563      rows[i] = LongValue.create(i);
2564
2565    java.util.Arrays.sort(rows, new MultiSortComparator(env, arrays));
2566
2567    // apply the permuation
2568
for(int i=0; i<arrays.length; i++)
2569      if (arrays[i] instanceof ArrayValue)
2570    permute(env, (ArrayValue)arrays[i], rows);
2571
2572    return true;
2573  }
2574
2575  /*
2576   * Apply a permutation to an array; on return, each element of
2577   * array[i] holds the value that was in array[permutation[i]]
2578   * before the call.
2579   */

2580  private static void permute(Env env, ArrayValue array,
2581                  LongValue[] permutation)
2582  {
2583    Value[] values = array.getValueArray(env);
2584    for(int i=0; i<permutation.length; i++)
2585      array.put(LongValue.create(i),
2586        values[(int)permutation[i].toLong()]);
2587  }
2588
2589
2590  // XXX: Performance Test asort
2591
/**
2592   * Sorts the array.
2593   */

2594  /*public Value asort(Env env,
2595             Value value,
2596             @Optional int mode)
2597  {
2598    if (! (value instanceof ArrayValue)) {
2599      env.warning(L.l("asort requires array at '{0}'", value));
2600      return BooleanValue.FALSE;
2601    }
2602
2603    ArrayValue array = (ArrayValue) value;
2604
2605    array.asort();
2606
2607    return BooleanValue.TRUE;
2608  }*/

2609
2610  // XXX: Performance Test ksort
2611
/**
2612   * Sorts the array.
2613   */

2614  /*public Value ksort(Env env,
2615             Value value,
2616             @Optional int mode)
2617  {
2618    if (! (value instanceof ArrayValue)) {
2619      env.warning(L.l("asort requires array at '{0}'", value));
2620      return BooleanValue.FALSE;
2621    }
2622
2623    ArrayValue array = (ArrayValue) value;
2624
2625    array.ksort();
2626
2627    return BooleanValue.TRUE;
2628  }*/

2629
2630  /**
2631   * Creates an array with all the values of the first array that are not
2632   * present in the other arrays, using a provided callback function to
2633   * determine equivalence.
2634   *
2635   * @param arrays first array is checked against the rest. Last element is the
2636   * callback function.
2637   * @return an array with all the values of the first array that are not in the
2638   * rest
2639   */

2640  public Value array_udiff(Env env, Value[] arrays)
2641  {
2642    if (arrays.length < 3) {
2643      env.warning("Wrong paremeter count for array_udiff()");
2644
2645      return NullValue.NULL;
2646    }
2647
2648    if (! (arrays[0] instanceof ArrayValue)) {
2649      env.warning("Argument #1 is not an array");
2650
2651      return NullValue.NULL;
2652    }
2653
2654    ArrayValue array = (ArrayValue) arrays[0];
2655
2656    Value callbackValue = arrays[arrays.length - 1];
2657
2658    Callback cmp;
2659
2660    try {
2661      cmp = env.createCallback(callbackValue);
2662    }
2663    catch (Throwable JavaDoc t) {
2664      log.log(Level.WARNING, t.toString(), t);
2665
2666      env.warning("Not a valid callback " + callbackValue.toString());
2667
2668      return NullValue.NULL;
2669    }
2670
2671    if (cmp == null) {
2672      env.warning("Not a valid callback " + callbackValue.toString());
2673
2674      return NullValue.NULL;
2675    }
2676
2677    ArrayValue diffArray = new ArrayValueImpl();
2678
2679    boolean isFound = false;
2680
2681    for (Value entryKey : array.keySet()) {
2682      Value entryValue = array.get(entryKey);
2683
2684      for (int k = 1; k < arrays.length - 1 && ! isFound; k++) {
2685        if (! (arrays[k] instanceof ArrayValue)) {
2686          env.warning("Argument #" + (k + 1) + " is not an array");
2687
2688          return NullValue.NULL;
2689        }
2690
2691        ArrayValue checkArray = (ArrayValue) arrays[k];
2692
2693        for (Map.Entry JavaDoc<Value, Value> entry : checkArray.entrySet()) {
2694          try {
2695            isFound = cmp.call(env, entryValue, entry.getValue()).toLong() == 0;
2696          }
2697          catch (Throwable JavaDoc t) {
2698            log.log(Level.WARNING, t.toString(), t);
2699
2700            env.warning("An error occurred while invoking the filter callback");
2701
2702            return NullValue.NULL;
2703          }
2704
2705          if (isFound)
2706            break;
2707        }
2708      }
2709
2710      if (! isFound)
2711        diffArray.put(entryKey, entryValue);
2712
2713      isFound = false;
2714    }
2715
2716    return diffArray;
2717  }
2718
2719  /**
2720   * Creates an array with all the values of the first array that are not
2721   * present in the other arrays, using a provided callback function to
2722   * determine equivalence. Also checks the key for equality using an internal
2723   * comparison function.
2724   *
2725   * @param arrays first array is checked against the rest. Last element is the
2726   * callback function.
2727   * @return an array with all the values of the first array that are not in the
2728   * rest
2729   */

2730  public Value array_udiff_assoc(Env env, Value[] arrays)
2731  {
2732    if (arrays.length < 3) {
2733      env.warning("Wrong paremeter count for array_udiff_assoc()");
2734
2735      return NullValue.NULL;
2736    }
2737
2738    if (! (arrays[0] instanceof ArrayValue)) {
2739      env.warning("Argument #1 is not an array");
2740
2741      return NullValue.NULL;
2742    }
2743
2744    ArrayValue array = (ArrayValue) arrays[0];
2745
2746    Value callbackValue = arrays[arrays.length - 1];
2747
2748    Callback cmp;
2749
2750    try {
2751      cmp = env.createCallback(callbackValue);
2752    }
2753    catch (Throwable JavaDoc t) {
2754      log.log(Level.WARNING, t.toString(), t);
2755
2756      env.warning("Not a valid callback " + callbackValue.toString());
2757
2758      return NullValue.NULL;
2759    }
2760
2761    if (cmp == null) {
2762      env.warning("Not a valid callback " + callbackValue.toString());
2763
2764      return NullValue.NULL;
2765    }
2766
2767    ArrayValue diffArray = new ArrayValueImpl();
2768
2769    boolean isFound = false;
2770
2771    for (Value entryKey : array.keySet()) {
2772      Value entryValue = array.get(entryKey);
2773
2774      for (int k = 1; k < arrays.length - 1 && ! isFound; k++) {
2775        if (! (arrays[k] instanceof ArrayValue)) {
2776          env.warning("Argument #" + (k + 1) + " is not an array");
2777
2778          return NullValue.NULL;
2779        }
2780
2781        ArrayValue checkArray = (ArrayValue) arrays[k];
2782
2783        for (Map.Entry JavaDoc<Value, Value> entry : checkArray.entrySet()) {
2784          try {
2785            boolean keyFound = entryKey.eql(entry.getKey());
2786
2787            boolean valueFound = false;
2788
2789            if (keyFound)
2790              valueFound = cmp.call(env, entryValue, entry.getValue())
2791                .toLong() == 0;
2792
2793            isFound = keyFound && valueFound;
2794          }
2795          catch (Throwable JavaDoc t) {
2796            log.log(Level.WARNING, t.toString(), t);
2797
2798            env.warning("An error occurred while invoking the filter callback");
2799
2800            return NullValue.NULL;
2801          }
2802
2803          if (isFound)
2804            break;
2805        }
2806      }
2807
2808      if (! isFound)
2809        diffArray.put(entryKey, entryValue);
2810
2811      isFound = false;
2812    }
2813
2814    return diffArray;
2815  }
2816
2817  /**
2818   * Creates an array with all the values of the first array that are not
2819   * present in the other arrays, using a provided callback function to
2820   * determine equivalence. Also checks keys using a provided callback
2821   * function.
2822   *
2823   * @param arrays first array is checked against the rest. Last two elementare
2824   * the callback functions.
2825   * @return an array with all the values of the first array that are not in the
2826   * rest
2827   */

2828  public Value array_udiff_uassoc(Env env, Value[] arrays)
2829  {
2830    if (arrays.length < 4) {
2831      env.warning("Wrong paremeter count for array_udiff_uassoc()");
2832
2833      return NullValue.NULL;
2834    }
2835
2836    if (! (arrays[0] instanceof ArrayValue)) {
2837      env.warning("Argument #1 is not an array");
2838
2839      return NullValue.NULL;
2840    }
2841
2842    ArrayValue array = (ArrayValue) arrays[0];
2843
2844    Value callbackValue = arrays[arrays.length - 2];
2845
2846    Callback cmpValue;
2847
2848    try {
2849      cmpValue = env.createCallback(callbackValue);
2850    }
2851    catch (Throwable JavaDoc t) {
2852      log.log(Level.WARNING, t.toString(), t);
2853
2854      env.warning("Not a valid callback " + callbackValue.toString());
2855
2856      return NullValue.NULL;
2857    }
2858
2859    if (cmpValue == null) {
2860      env.warning("Not a valid callback " + callbackValue.toString());
2861
2862      return NullValue.NULL;
2863    }
2864
2865    Value callbackKey = arrays[arrays.length - 1];
2866
2867    Callback cmpKey;
2868
2869    try {
2870      cmpKey = env.createCallback(callbackKey);
2871    }
2872    catch (Throwable JavaDoc t) {
2873      log.log(Level.WARNING, t.toString(), t);
2874
2875      env.warning("Not a valid callback " + callbackKey.toString());
2876
2877      return NullValue.NULL;
2878    }
2879
2880    if (cmpKey == null) {
2881      env.warning("Not a valid callback " + callbackKey.toString());
2882
2883      return NullValue.NULL;
2884    }
2885
2886    ArrayValue diffArray = new ArrayValueImpl();
2887
2888    boolean isFound = false;
2889
2890    for (Value entryKey : array.keySet()) {
2891      Value entryValue = array.get(entryKey);
2892
2893      for (int k = 1; k < arrays.length - 2 && ! isFound; k++) {
2894        if (! (arrays[k] instanceof ArrayValue)) {
2895          env.warning("Argument #" + (k + 1) + " is not an array");
2896
2897          return NullValue.NULL;
2898        }
2899
2900        ArrayValue checkArray = (ArrayValue) arrays[k];
2901
2902        for (Map.Entry JavaDoc<Value, Value> entry : checkArray.entrySet()) {
2903          try {
2904            boolean valueFound =
2905              cmpValue.call(env, entryValue, entry.getValue()).toLong() == 0;
2906
2907            boolean keyFound = false;
2908
2909            if (valueFound)
2910              keyFound = cmpKey.call(env, entryKey, entry.getKey()).toLong() ==
2911                         0;
2912
2913            isFound = valueFound && keyFound;
2914          }
2915          catch (Throwable JavaDoc t) {
2916            log.log(Level.WARNING, t.toString(), t);
2917
2918            env.warning("An error occurred while invoking the filter callback");
2919
2920            return NullValue.NULL;
2921          }
2922
2923          if (isFound)
2924            break;
2925        }
2926      }
2927
2928      if (! isFound)
2929        diffArray.put(entryKey, entryValue);
2930
2931      isFound = false;
2932    }
2933
2934    return diffArray;
2935  }
2936
2937  /**
2938   * Creates an array with all the values of the first array that are present in
2939   * the other arrays, using a provided callback function to determine
2940   * equivalence.
2941   *
2942   * @param arrays first array is checked against the rest. Last element is the
2943   * callback function.
2944   * @return an array with all the values of the first array that are in the
2945   * rest
2946   */

2947  public Value array_uintersect(Env env, Value[] arrays)
2948  {
2949    if (arrays.length < 3) {
2950      env.warning("Wrong paremeter count for array_uintersect()");
2951
2952      return NullValue.NULL;
2953    }
2954
2955    if (! (arrays[0] instanceof ArrayValue)) {
2956      env.warning("Argument #1 is not an array");
2957
2958      return NullValue.NULL;
2959    }
2960
2961    ArrayValue array = (ArrayValue) arrays[0];
2962
2963    Value callbackValue = arrays[arrays.length - 1];
2964
2965    Callback cmp;
2966
2967    try {
2968      cmp = env.createCallback(callbackValue);
2969    }
2970    catch (Throwable JavaDoc t) {
2971      log.log(Level.WARNING, t.toString(), t);
2972
2973      env.warning("Not a valid callback " + callbackValue.toString());
2974
2975      return NullValue.NULL;
2976    }
2977
2978    if (cmp == null) {
2979      env.warning("Not a valid callback " + callbackValue.toString());
2980
2981      return NullValue.NULL;
2982    }
2983
2984    ArrayValue interArray = new ArrayValueImpl();
2985
2986    boolean isFound = true;
2987
2988    for (Value entryKey : array.keySet()) {
2989      Value entryValue = array.get(entryKey);
2990
2991      for (int k = 1; k < arrays.length - 1 && isFound; k++) {
2992        if (! (arrays[k] instanceof ArrayValue)) {
2993          env.warning("Argument #" + (k + 1) + " is not an array");
2994
2995          return NullValue.NULL;
2996        }
2997
2998        ArrayValue checkArray = (ArrayValue) arrays[k];
2999
3000        for (Map.Entry JavaDoc<Value, Value> entry : checkArray.entrySet()) {
3001          try {
3002            isFound = cmp.call(env, entryValue, entry.getValue()).toLong() == 0;
3003          }
3004          catch (Throwable JavaDoc t) {
3005            log.log(Level.WARNING, t.toString(), t);
3006
3007            env.warning("An error occurred while invoking the filter callback");
3008
3009            return NullValue.NULL;
3010          }
3011
3012          if (isFound)
3013            break;
3014        }
3015      }
3016
3017      if (isFound)
3018        interArray.put(entryKey, entryValue);
3019    }
3020
3021    return interArray;
3022  }
3023
3024  /**
3025   * Creates an array with all the values of the first array that are present in
3026   * the other arrays, using a provided callback function to determine
3027   * equivalence. Also checks the keys for equivalence using an internal
3028   * comparison.
3029   *
3030   * @param arrays first array is checked against the rest. Last element is the
3031   * callback function.
3032   * @return an array with all the values of the first array that are in the
3033   * rest
3034   */

3035  public Value array_uintersect_assoc(Env env, Value[] arrays)
3036  {
3037    if (arrays.length < 3) {
3038      env.warning("Wrong paremeter count for array_uintersect_assoc()");
3039
3040      return NullValue.NULL;
3041    }
3042
3043    if (! (arrays[0] instanceof ArrayValue)) {
3044      env.warning("Argument #1 is not an array");
3045
3046      return NullValue.NULL;
3047    }
3048
3049    ArrayValue array = (ArrayValue) arrays[0];
3050
3051    Value callbackValue = arrays[arrays.length - 1];
3052
3053    Callback cmp;
3054
3055    try {
3056      cmp = env.createCallback(callbackValue);
3057    }
3058    catch (Throwable JavaDoc t) {
3059      log.log(Level.WARNING, t.toString(), t);
3060
3061      env.warning("Not a valid callback " + callbackValue.toString());
3062
3063      return NullValue.NULL;
3064    }
3065
3066    if (cmp == null) {
3067      env.warning("Not a valid callback " + callbackValue.toString());
3068
3069      return NullValue.NULL;
3070    }
3071
3072    ArrayValue interArray = new ArrayValueImpl();
3073
3074    boolean isFound = true;
3075
3076    for (Value entryKey : array.keySet()) {
3077      Value entryValue = array.get(entryKey);
3078
3079      for (int k = 1; k < arrays.length - 1 && isFound; k++) {
3080        if (! (arrays[k] instanceof ArrayValue)) {
3081          env.warning("Argument #" + (k + 1) + " is not an array");
3082
3083          return NullValue.NULL;
3084        }
3085
3086        ArrayValue checkArray = (ArrayValue) arrays[k];
3087
3088        for (Map.Entry JavaDoc<Value, Value> entry : checkArray.entrySet()) {
3089          try {
3090            boolean keyFound = entryKey.eql(entry.getKey());
3091
3092            boolean valueFound = false;
3093
3094            if (keyFound)
3095              valueFound = cmp.call(env, entryValue, entry.getValue())
3096                .toLong() == 0;
3097
3098            isFound = keyFound && valueFound;
3099          }
3100          catch (Throwable JavaDoc t) {
3101            log.log(Level.WARNING, t.toString(), t);
3102
3103            env.warning("An error occurred while invoking the filter callback");
3104
3105            return NullValue.NULL;
3106          }
3107
3108          if (isFound)
3109            break;
3110        }
3111      }
3112
3113      if (isFound)
3114        interArray.put(entryKey, entryValue);
3115    }
3116
3117    return interArray;
3118  }
3119
3120  /**
3121   * Creates an array with all the values of the first array that are present in
3122   * the other arrays, using a provided callback function to determine
3123   * equivalence. Also checks the keys for equivalence using a pass callback
3124   * function
3125   *
3126   * @param arrays first array is checked against the rest. Last two elements
3127   * are the callback functions.
3128   * @return an array with all the values of the first array that are in the
3129   * rest
3130   */

3131  public Value array_uintersect_uassoc(Env env, Value[] arrays)
3132  {
3133    if (arrays.length < 4) {
3134      env.warning("Wrong paremeter count for array_uintersect_uassoc()");
3135
3136      return NullValue.NULL;
3137    }
3138
3139    if (! (arrays[0] instanceof ArrayValue)) {
3140      env.warning("Argument #1 is not an array");
3141
3142      return NullValue.NULL;
3143    }
3144
3145    ArrayValue array = (ArrayValue) arrays[0];
3146
3147    Value callbackValue = arrays[arrays.length - 2];
3148
3149    Callback cmpValue;
3150
3151    try {
3152      cmpValue = env.createCallback(callbackValue);
3153    }
3154    catch (Throwable JavaDoc t) {
3155      log.log(Level.WARNING, t.toString(), t);
3156
3157      env.warning("Not a valid callback " + callbackValue.toString());
3158
3159      return NullValue.NULL;
3160    }
3161
3162    if (cmpValue == null) {
3163      env.warning("Not a valid callback " + callbackValue.toString());
3164
3165      return NullValue.NULL;
3166    }
3167
3168    Value callbackKey = arrays[arrays.length - 1];
3169
3170    Callback cmpKey;
3171
3172    try {
3173      cmpKey = env.createCallback(callbackKey);
3174    }
3175    catch (Throwable JavaDoc t) {
3176      log.log(Level.WARNING, t.toString(), t);
3177
3178      env.warning("Not a valid callback " + callbackKey.toString());
3179
3180      return NullValue.NULL;
3181    }
3182
3183    if (cmpKey == null) {
3184      env.warning("Not a valid callback " + callbackKey.toString());
3185
3186      return NullValue.NULL;
3187    }
3188
3189    ArrayValue interArray = new ArrayValueImpl();
3190
3191    boolean isFound = true;
3192
3193    for (Value entryKey : array.keySet()) {
3194      Value entryValue = array.get(entryKey);
3195
3196      for (int k = 1; k < arrays.length - 2 && isFound; k++) {
3197        if (! (arrays[k] instanceof ArrayValue)) {
3198          env.warning("Argument #" + (k + 1) + " is not an array");
3199
3200          return NullValue.NULL;
3201        }
3202
3203        ArrayValue checkArray = (ArrayValue) arrays[k];
3204
3205        for (Map.Entry JavaDoc<Value, Value> entry : checkArray.entrySet()) {
3206          try {
3207            boolean valueFound =
3208              cmpValue.call(env, entryValue, entry.getValue()).toLong() == 0;
3209
3210            boolean keyFound = false;
3211
3212            if (valueFound)
3213              keyFound = cmpKey.call(env, entryKey, entry.getKey()).toLong() ==
3214                         0;
3215
3216            isFound = valueFound && keyFound;
3217          }
3218          catch (Throwable JavaDoc t) {
3219            log.log(Level.WARNING, t.toString(), t);
3220
3221            env.warning("An error occurred while invoking the filter callback");
3222
3223            return NullValue.NULL;
3224          }
3225
3226          if (isFound)
3227            break;
3228        }
3229      }
3230
3231      if (isFound)
3232        interArray.put(entryKey, entryValue);
3233    }
3234
3235    return interArray;
3236  }
3237
3238  /**
3239   * Creates an array of corresponding values to variables in the symbol name.
3240   * The passed parameters are the names of the variables to be added to the
3241   * array.
3242   *
3243   * @param variables contains the names of variables to add to the array
3244   * @return an array with the values of variables that match those passed
3245   */

3246  @UsesSymbolTable
3247  public ArrayValue compact(Env env, Value[] variables)
3248  {
3249    ArrayValue compactArray = new ArrayValueImpl();
3250
3251    for (Value variableName : variables) {
3252      if (variableName instanceof StringValue) {
3253        Value tableValue = env.getValue(variableName.toString());
3254
3255        if (tableValue.isset())
3256          compactArray.put(variableName, tableValue);
3257      }
3258      else if (variableName instanceof ArrayValue) {
3259        ArrayValue array = (ArrayValue) variableName;
3260
3261        ArrayValue innerArray = compact(env, array.valuesToArray());
3262
3263        compactArray.putAll(innerArray);
3264      }
3265    }
3266
3267    return compactArray;
3268  }
3269
3270  /**
3271   * Returns the size of the array.
3272   */

3273  public static Value sizeof(@ReadOnly Value value)
3274  {
3275    return count(value);
3276  }
3277
3278  private static class CompareString
3279    implements
3280    Comparator<Map.Entry JavaDoc<Value, Value>>
3281  {
3282    private AbstractGet _getter;
3283
3284    private int _order;
3285
3286    CompareString(AbstractGet getter, int order)
3287    {
3288      _getter = getter;
3289      _order = order;
3290    }
3291
3292    public int compare(Map.Entry JavaDoc<Value, Value> aEntry,
3293                       Map.Entry JavaDoc<Value, Value> bEntry)
3294    {
3295      String JavaDoc aElement = _getter.get(aEntry).toString();
3296      String JavaDoc bElement = _getter.get(bEntry).toString();
3297
3298      return aElement.compareTo(bElement) * _order;
3299    }
3300  }
3301
3302  private static class CompareNumeric
3303    implements
3304    Comparator<Map.Entry JavaDoc<Value, Value>>
3305  {
3306    private AbstractGet _getter;
3307
3308    private int _order;
3309
3310    CompareNumeric(AbstractGet getter, int order)
3311    {
3312      _getter = getter;
3313      _order = order;
3314    }
3315
3316    public int compare(Map.Entry JavaDoc<Value, Value> aEntry,
3317                       Map.Entry JavaDoc<Value, Value> bEntry)
3318    {
3319      try {
3320        long aElement = _getter.get(aEntry).toLong();
3321        long bElement = _getter.get(bEntry).toLong();
3322
3323        if (aElement == bElement)
3324          return 0;
3325        else if (aElement < bElement)
3326          return -1 * _order;
3327        else
3328          return _order;
3329      }
3330      catch (Throwable JavaDoc e) {
3331        throw new RuntimeException JavaDoc(e);
3332      }
3333    }
3334  }
3335
3336  private static class CompareLocale
3337    implements
3338    Comparator<Map.Entry JavaDoc<Value, Value>>
3339  {
3340    private AbstractGet _getter;
3341
3342    private int _order;
3343
3344    private Collator JavaDoc _collator;
3345
3346    CompareLocale(AbstractGet getter, int order, Collator JavaDoc collator)
3347    {
3348      _getter = getter;
3349      _order = order;
3350      _collator = collator;
3351    }
3352
3353    public int compare(Map.Entry JavaDoc<Value, Value> aEntry,
3354                       Map.Entry JavaDoc<Value, Value> bEntry)
3355    {
3356      String JavaDoc aElement = _getter.get(aEntry).toString();
3357      String JavaDoc bElement = _getter.get(bEntry).toString();
3358
3359      return _collator.compare(aElement, bElement) * _order;
3360    }
3361  }
3362
3363  private static class CompareNormal
3364    implements Comparator<Map.Entry JavaDoc<Value, Value>>
3365  {
3366    private AbstractGet _getter;
3367
3368    private int _order;
3369
3370    CompareNormal(AbstractGet getter, int order)
3371    {
3372      _getter = getter;
3373      _order = order;
3374    }
3375
3376    public int compare(Map.Entry JavaDoc<Value, Value> aEntry,
3377                       Map.Entry JavaDoc<Value, Value> bEntry)
3378    {
3379      if (_getter instanceof GetKey) {
3380        KeyComparator k = KeyComparator.CMP;
3381
3382        return k.compare(aEntry, bEntry) * _order;
3383      }
3384
3385      ValueComparator c = ValueComparator.CMP;
3386
3387      return c.compare(aEntry, bEntry) * _order;
3388    }
3389  }
3390
3391  private static class CompareNatural
3392    implements
3393    Comparator<Map.Entry JavaDoc<Value, Value>>
3394  {
3395    private AbstractGet _getter;
3396
3397    private int _order;
3398
3399    private boolean _isCaseSensitive;
3400
3401    CompareNatural(AbstractGet getter, int order, boolean isCaseSensitive)
3402    {
3403      _getter = getter;
3404      _order = order;
3405      _isCaseSensitive = isCaseSensitive;
3406    }
3407
3408    public int compare(Map.Entry JavaDoc<Value, Value> aEntry,
3409                       Map.Entry JavaDoc<Value, Value> bEntry)
3410    {
3411      try {
3412        String JavaDoc aElement = _getter.get(aEntry).toString();
3413        String JavaDoc bElement = _getter.get(bEntry).toString();
3414
3415        if (! _isCaseSensitive) {
3416          aElement = aElement.toLowerCase();
3417          bElement = bElement.toLowerCase();
3418        }
3419
3420        StringParser aParser = new StringParser(aElement);
3421        StringParser bParser = new StringParser(bElement);
3422
3423        while (aParser.hasNext() && bParser.hasNext()) {
3424          String JavaDoc aPart = aParser.next();
3425          String JavaDoc bPart = bParser.next();
3426
3427          int comparison;
3428
3429          try {
3430            Long JavaDoc aLong = Long.valueOf(aPart);
3431            Long JavaDoc bLong = Long.valueOf(bPart);
3432
3433            comparison = aLong.compareTo(bLong);
3434          }
3435          catch (NumberFormatException JavaDoc e) {
3436            comparison = aPart.compareTo(bPart);
3437          }
3438
3439          if (comparison < 0)
3440            return -1;
3441          else if (comparison > 0)
3442            return 1;
3443        }
3444
3445        if (bParser.hasNext())
3446          return 1;
3447        else if (aParser.hasNext())
3448          return -1;
3449        else
3450          return 0;
3451
3452      }
3453      catch (Throwable JavaDoc e) {
3454        throw new RuntimeException JavaDoc(e);
3455      }
3456    }
3457  }
3458
3459  private static class CompareCallBack
3460    implements Comparator<Map.Entry JavaDoc<Value, Value>>
3461  {
3462    private AbstractGet _getter;
3463
3464    private int _order;
3465
3466    private Callback _func;
3467
3468    private Env _env;
3469
3470    CompareCallBack(AbstractGet getter, int order, Callback func,
3471                    Env env)
3472    {
3473      _getter = getter;
3474      _order = order;
3475      _func = func;
3476      _env = env;
3477    }
3478
3479    public int compare(Map.Entry JavaDoc<Value, Value> aEntry,
3480                       Map.Entry JavaDoc<Value, Value> bEntry)
3481    {
3482      try {
3483        Value aElement = _getter.get(aEntry);
3484        Value bElement = _getter.get(bEntry);
3485
3486        return (int) _func.call(_env, aElement, bElement).toLong();
3487      }
3488      catch (Exception JavaDoc e) {
3489        throw new QuercusModuleException(e);
3490      }
3491    }
3492  }
3493
3494  /*
3495   * A comparator used to sort a permutation based on a set of
3496   * column-arrays.
3497   */

3498  private static class MultiSortComparator
3499    implements Comparator<LongValue>
3500  {
3501
3502    private final Env _env;
3503    private final Value[] _arrays;
3504
3505    public MultiSortComparator(Env env, Value[] arrays)
3506    {
3507      this._env = env;
3508      this._arrays = arrays;
3509    }
3510
3511    /*
3512     * Examine the "row" consisting of arrays[x][index1] and
3513     * arrays[x][index2] for all indices "x"; the permutation will be
3514     * sorted according to this comparison.
3515     */

3516    public int compare(LongValue index1, LongValue index2)
3517    {
3518      for(int i=0; i<_arrays.length; i++) {
3519
3520    // reset direction/mode for each array (per the php.net spec)
3521
int direction = SORT_ASC;
3522    int mode = SORT_REGULAR;
3523    ArrayValue av = (ArrayValue)_arrays[i];
3524
3525    // process all flags appearing *after* an array but before the next one
3526
while((i+1)<_arrays.length && _arrays[i+1] instanceof LongValue) {
3527      switch(_arrays[++i].toInt()) {
3528        case SORT_ASC: direction = SORT_ASC; break;
3529        case SORT_DESC: direction = SORT_DESC; break;
3530        case SORT_REGULAR: mode = SORT_REGULAR; break;
3531        case SORT_STRING: mode = SORT_STRING; break;
3532        case SORT_NUMERIC: mode = SORT_NUMERIC; break;
3533        default: _env.warning("Unknown sort flag: " + _arrays[i]);
3534      }
3535    }
3536
3537    Value v1 = av.get(index1);
3538    Value v2 = av.get(index2);
3539
3540    if (mode==SORT_STRING) {
3541      v1 = v1.toStringValue();
3542      v2 = v2.toStringValue();
3543    } else if (mode==SORT_NUMERIC) {
3544      v1 = LongValue.create(v1.toLong());
3545      v2 = LongValue.create(v2.toLong());
3546    }
3547
3548    if (v1.lt(v2)) return direction==SORT_ASC ? -1 : 1;
3549    if (v1.gt(v2)) return direction==SORT_ASC ? 1 : -1;
3550
3551      }
3552      return 0;
3553    }
3554  }
3555
3556  private static class StringParser {
3557    private int _current;
3558    private int _length;
3559
3560    private String JavaDoc _string;
3561
3562    private static final int SYMBOL = 1;
3563    private static final int LETTER = 2;
3564    private static final int DIGIT = 3;
3565
3566    StringParser(String JavaDoc string)
3567    {
3568      _string = string;
3569      _length = string.length();
3570      _current = 0;
3571    }
3572
3573    public boolean hasNext()
3574    {
3575      return _current < _length;
3576    }
3577
3578    public String JavaDoc next()
3579    {
3580      int start;
3581      int type;
3582
3583      try {
3584        char character = _string.charAt(_current);
3585
3586        if (character == '0') {
3587          _current++;
3588          return "0";
3589        }
3590        else if (Character.isLetter(character))
3591          type = LETTER;
3592        else if (Character.isDigit(character))
3593          type = DIGIT;
3594        else
3595          type = SYMBOL;
3596
3597        for (start = _current; _current < _length; _current++) {
3598          if (type == LETTER && Character.isLetter(_string.charAt(_current)))
3599          {
3600          }
3601          else if (type == DIGIT && Character.isDigit(_string.charAt(_current)))
3602          {
3603          }
3604          else if (type == SYMBOL &&
3605                   !Character.isLetterOrDigit(_string.charAt(_current))) {
3606          }
3607          else
3608            break;
3609        }
3610
3611        return _string.substring(start, _current);
3612      }
3613      catch (Exception JavaDoc e) {
3614        log.log(Level.WARNING, e.toString(), e);
3615        return null;
3616      }
3617    }
3618  }
3619}
3620
Popular Tags