0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.quercus.lib;
0031:
0032: import com.caucho.quercus.QuercusModuleException;
0033: import com.caucho.quercus.annotation.Optional;
0034: import com.caucho.quercus.annotation.ReadOnly;
0035: import com.caucho.quercus.annotation.Reference;
0036: import com.caucho.quercus.annotation.UsesSymbolTable;
0037: import com.caucho.quercus.annotation.NotNull;
0038: import com.caucho.quercus.env.*;
0039: import com.caucho.quercus.env.ArrayValue.AbstractGet;
0040: import com.caucho.quercus.env.ArrayValue.GetKey;
0041: import com.caucho.quercus.env.ArrayValue.KeyComparator;
0042: import com.caucho.quercus.env.ArrayValue.ValueComparator;
0043: import com.caucho.quercus.module.AbstractQuercusModule;
0044: import com.caucho.quercus.program.AbstractFunction;
0045: import com.caucho.util.L10N;
0046: import com.caucho.util.RandomUtil;
0047:
0048: import java.text.Collator;
0049: import java.util.Comparator;
0050: import java.util.Iterator;
0051: import java.util.Locale;
0052: import java.util.Map;
0053: import java.util.logging.Level;
0054: import java.util.logging.Logger;
0055:
0056: /**
0057: * PHP array routines.
0058: */
0059: public class ArrayModule extends AbstractQuercusModule {
0060: private static final L10N L = new L10N(ArrayModule.class);
0061:
0062: private static final Logger log = Logger
0063: .getLogger(ArrayModule.class.getName());
0064:
0065: public static final int CASE_UPPER = 2;
0066: public static final int CASE_LOWER = 1;
0067:
0068: public static final int SORT_REGULAR = 0;
0069: public static final int SORT_NUMERIC = 1;
0070: public static final int SORT_STRING = 2;
0071: public static final int SORT_LOCALE_STRING = 5;
0072: public static final int SORT_NORMAL = 1;
0073: public static final int SORT_REVERSE = -1;
0074:
0075: public static final int SORT_DESC = 3;
0076: public static final int SORT_ASC = 4;
0077:
0078: public static final int EXTR_OVERWRITE = 0;
0079: public static final int EXTR_SKIP = 1;
0080: public static final int EXTR_PREFIX_SAME = 2;
0081: public static final int EXTR_PREFIX_ALL = 3;
0082: public static final int EXTR_PREFIX_INVALID = 4;
0083: public static final int EXTR_IF_EXISTS = 6;
0084: public static final int EXTR_PREFIX_IF_EXISTS = 5;
0085: public static final int EXTR_REFS = 256;
0086:
0087: public static final boolean CASE_SENSITIVE = true;
0088: public static final boolean CASE_INSENSITIVE = false;
0089: public static final boolean KEY_RESET = true;
0090: public static final boolean NO_KEY_RESET = false;
0091: public static final boolean STRICT = true;
0092: public static final boolean NOT_STRICT = false;
0093:
0094: private static final CompareString CS_VALUE_NORMAL = new CompareString(
0095: ArrayValue.GET_VALUE, SORT_NORMAL);
0096:
0097: private static final CompareString CS_VALUE_REVERSE = new CompareString(
0098: ArrayValue.GET_VALUE, SORT_REVERSE);
0099:
0100: private static final CompareString CS_KEY_NORMAL = new CompareString(
0101: ArrayValue.GET_KEY, SORT_NORMAL);
0102:
0103: private static final CompareString CS_KEY_REVERSE = new CompareString(
0104: ArrayValue.GET_KEY, SORT_REVERSE);
0105:
0106: private static final CompareNumeric CN_VALUE_NORMAL = new CompareNumeric(
0107: ArrayValue.GET_VALUE, SORT_NORMAL);
0108:
0109: private static final CompareNumeric CN_VALUE_REVERSE = new CompareNumeric(
0110: ArrayValue.GET_VALUE, SORT_REVERSE);
0111:
0112: private static final CompareNumeric CN_KEY_NORMAL = new CompareNumeric(
0113: ArrayValue.GET_KEY, SORT_NORMAL);
0114:
0115: private static final CompareNumeric CN_KEY_REVERSE = new CompareNumeric(
0116: ArrayValue.GET_KEY, SORT_REVERSE);
0117:
0118: private static final CompareNormal CNO_VALUE_NORMAL = new CompareNormal(
0119: ArrayValue.GET_VALUE, SORT_NORMAL);
0120:
0121: private static final CompareNormal CNO_VALUE_REVERSE = new CompareNormal(
0122: ArrayValue.GET_VALUE, SORT_REVERSE);
0123:
0124: private static final CompareNormal CNO_KEY_NORMAL = new CompareNormal(
0125: ArrayValue.GET_KEY, SORT_NORMAL);
0126:
0127: private static final CompareNormal CNO_KEY_REVERSE = new CompareNormal(
0128: ArrayValue.GET_KEY, SORT_REVERSE);
0129:
0130: private static final CompareNatural CNA_VALUE_NORMAL_SENSITIVE = new CompareNatural(
0131: ArrayValue.GET_VALUE, SORT_NORMAL, CASE_SENSITIVE);
0132:
0133: private static final CompareNatural CNA_VALUE_NORMAL_INSENSITIVE = new CompareNatural(
0134: ArrayValue.GET_VALUE, SORT_NORMAL, CASE_INSENSITIVE);
0135:
0136: /**
0137: * Returns true for the mysql extension.
0138: */
0139: public String[] getLoadedExtensions() {
0140: return new String[] { "standard" };
0141: }
0142:
0143: /**
0144: * Changes the key case
0145: */
0146: public Value array_change_key_case(Env env, ArrayValue array,
0147: @Optional("CASE_LOWER")
0148: int toCase) {
0149: if (array == null)
0150: return BooleanValue.FALSE;
0151:
0152: ArrayValue newArray = new ArrayValueImpl();
0153:
0154: for (Map.Entry<Value, Value> entry : array.entrySet()) {
0155: Value keyValue = entry.getKey();
0156:
0157: if (keyValue instanceof StringValue) {
0158: String key = keyValue.toString();
0159:
0160: if (toCase == CASE_UPPER)
0161: key = key.toUpperCase();
0162: else
0163: key = key.toLowerCase();
0164:
0165: newArray.put(env.createString(key), entry.getValue());
0166: } else
0167: newArray.put(keyValue, entry.getValue());
0168: }
0169:
0170: return newArray;
0171: }
0172:
0173: /**
0174: * Chunks the array
0175: */
0176: public Value array_chunk(Env env, ArrayValue array, int size,
0177: @Optional
0178: boolean preserveKeys) {
0179: if (array == null)
0180: return NullValue.NULL;
0181:
0182: ArrayValue newArray = new ArrayValueImpl();
0183: ArrayValue currentArray = null;
0184:
0185: if (size < 1) {
0186: env.warning("Size parameter expected to be greater than 0");
0187:
0188: return NullValue.NULL;
0189: }
0190:
0191: int i = 0;
0192: for (Map.Entry<Value, Value> entry : array.entrySet()) {
0193: Value key = entry.getKey();
0194: Value value = entry.getKey();
0195:
0196: if (i % size == 0) {
0197: currentArray = new ArrayValueImpl();
0198: newArray.put(currentArray);
0199: }
0200:
0201: if (preserveKeys)
0202: currentArray.put(key, value);
0203: else
0204: currentArray.put(new LongValue(i % size), value);
0205:
0206: i++;
0207: }
0208:
0209: return newArray;
0210: }
0211:
0212: /**
0213: * Combines array
0214: */
0215: public Value array_combine(Env env, ArrayValue keys,
0216: ArrayValue values) {
0217: if (keys == null || values == null)
0218: return BooleanValue.FALSE;
0219:
0220: if (keys.getSize() < 1 || values.getSize() < 1) {
0221: env
0222: .warning("Both parameters should have at least 1 element");
0223:
0224: return BooleanValue.FALSE;
0225: }
0226:
0227: if (keys.getSize() != values.getSize()) {
0228: env
0229: .warning("Both parameters should have equal number of elements");
0230:
0231: return BooleanValue.FALSE;
0232: }
0233:
0234: Iterator<Value> keyIter = keys.values().iterator();
0235: Iterator<Value> valueIter = values.values().iterator();
0236:
0237: ArrayValue array = new ArrayValueImpl();
0238:
0239: while (keyIter.hasNext() && valueIter.hasNext()) {
0240: array.put(keyIter.next(), valueIter.next());
0241: }
0242:
0243: return array;
0244: }
0245:
0246: /**
0247: * Counts the values
0248: */
0249: public Value array_count_values(Env env, ArrayValue array) {
0250: if (array == null)
0251: return NullValue.NULL;
0252:
0253: ArrayValue result = new ArrayValueImpl();
0254:
0255: for (Value value : array.values()) {
0256: if (!(value.isLongConvertible())
0257: && !(value instanceof StringValue))
0258: env
0259: .warning("Can only count STRING and INTEGER values!");
0260: else {
0261: Value count = result.get(value);
0262:
0263: if (count == null)
0264: count = new LongValue(1);
0265: else
0266: count = count.add(1);
0267:
0268: result.put(value, count);
0269: }
0270: }
0271:
0272: return result;
0273: }
0274:
0275: /**
0276: * Pops off the top element
0277: */
0278: public Value array_pop(Env env, @Reference
0279: Value value) {
0280: if (value.isArray()) {
0281: ArrayValue array = value.toArrayValue(env);
0282:
0283: if (array.getSize() <= 0)
0284: return NullValue.NULL;
0285:
0286: return array.pop();
0287: } else {
0288: env.warning(L.l("expected an array but saw {0}", value
0289: .toValue().getClass().getSimpleName()));
0290: return NullValue.NULL;
0291: }
0292: }
0293:
0294: /**
0295: * Returns the size of the array.
0296: */
0297: public static Value count(Env env, @ReadOnly
0298: Value value, @Optional("false")
0299: boolean recursive) {
0300: if (!recursive)
0301: return LongValue.create(value.getCount(env));
0302: else
0303: return LongValue.create(value.getCountRecursive(env));
0304: }
0305:
0306: /**
0307: * Returns the current value of the array.
0308: */
0309: public static Value current(@ReadOnly
0310: Value value) {
0311: if (value instanceof ArrayValue) {
0312: ArrayValue array = (ArrayValue) value;
0313:
0314: return array.current();
0315: } else
0316: return BooleanValue.FALSE;
0317: }
0318:
0319: /**
0320: * Returns the current key of the array.
0321: */
0322: public static Value key(@ReadOnly
0323: Value value) {
0324: if (value instanceof ArrayValue) {
0325: ArrayValue array = (ArrayValue) value;
0326:
0327: return array.key();
0328: } else
0329: return BooleanValue.FALSE;
0330: }
0331:
0332: /**
0333: * Returns the current value of the array.
0334: */
0335: public static Value pos(@ReadOnly
0336: Value value) {
0337: return current(value);
0338: }
0339:
0340: /**
0341: * Returns the next value of the array.
0342: */
0343: public static Value next(Value value) {
0344: if (value instanceof ArrayValue) {
0345: ArrayValue array = (ArrayValue) value;
0346:
0347: return array.next();
0348: } else
0349: return BooleanValue.FALSE;
0350: }
0351:
0352: /**
0353: * Returns the next value of the array.
0354: */
0355: public static Value each(Env env, @Reference
0356: Value value) {
0357: if (value instanceof Var) {
0358: value = value.toValue();
0359:
0360: if (value.isArray())
0361: return value.toArrayValue(env).each();
0362: else {
0363: L.l("each() requires argument to be an array");
0364: env.warning("each() requires argument to be an array");
0365:
0366: return NullValue.NULL;
0367: }
0368: } else {
0369: L.l("each() argument must be a variable");
0370: return env.error("each() argument must be a variable");
0371: }
0372: }
0373:
0374: /**
0375: * Returns the previous value of the array.
0376: */
0377: public static Value prev(Value value) {
0378: if (value instanceof ArrayValue) {
0379: ArrayValue array = (ArrayValue) value;
0380:
0381: return array.prev();
0382: } else
0383: return BooleanValue.FALSE;
0384: }
0385:
0386: /**
0387: * Resets the pointer
0388: */
0389: public static Value reset(Value value) {
0390: if (value instanceof ArrayValue) {
0391: ArrayValue array = (ArrayValue) value;
0392:
0393: return array.reset();
0394: } else
0395: return BooleanValue.FALSE;
0396: }
0397:
0398: /**
0399: * Returns the current value of the array.
0400: */
0401: public static Value shuffle(ArrayValue array) {
0402: if (array == null)
0403: return BooleanValue.FALSE;
0404:
0405: array.shuffle();
0406:
0407: return BooleanValue.TRUE;
0408: }
0409:
0410: /**
0411: * Resets the pointer to the end
0412: */
0413: public static Value end(Value value) {
0414: if (value instanceof ArrayValue) {
0415: ArrayValue array = (ArrayValue) value;
0416:
0417: return array.end();
0418: } else
0419: return BooleanValue.FALSE;
0420: }
0421:
0422: /**
0423: * Checks if the key is in the given array
0424: *
0425: * @param key a key to check for in the array
0426: * @param searchArray the array to search for the key in
0427: * @return true if the key is in the array, and false otherwise
0428: */
0429: public static boolean array_key_exists(Env env, @ReadOnly
0430: Value key, @ReadOnly
0431: Value searchArray) {
0432:
0433: if (!searchArray.isset() || !key.isset()) {
0434: return false;
0435: }
0436:
0437: if (!(searchArray.isArray() || searchArray.isObject())) {
0438: env
0439: .warning(L
0440: .l("'"
0441: + searchArray.toString()
0442: + "' is an unexpected argument, expected ArrayValue or ObjectValue"));
0443: return false;
0444: }
0445:
0446: if (!(key.isString() || key.isLongConvertible())) {
0447: env
0448: .warning(L
0449: .l(
0450: "The first argument (a '{0}') should be either a string or an integer",
0451: key.getType()));
0452: return false;
0453: }
0454:
0455: if (searchArray instanceof ArrayValue) {
0456: return ((ArrayValue) searchArray).containsKey(key) != null;
0457: } else {
0458: return !searchArray.getField(env, key.toStringValue())
0459: .isNull();
0460: }
0461: }
0462:
0463: /**
0464: * Undocumented alias for {@link #array_key_exists}.
0465: */
0466: public static boolean key_exists(Env env, @ReadOnly
0467: Value key, @ReadOnly
0468: Value searchArray) {
0469: return array_key_exists(env, key, searchArray);
0470: }
0471:
0472: /**
0473: * Returns an array of the keys in the given array
0474: *
0475: * @param array the array to obtain the keys for
0476: * @param searchValue the corresponding value of the returned key array
0477: * @return an array containing the keys
0478: */
0479: public Value array_keys(Env env, @ReadOnly
0480: ArrayValue array, @Optional
0481: @ReadOnly
0482: Value searchValue, @Optional
0483: boolean isStrict) {
0484: if (array == null)
0485: return NullValue.NULL;
0486:
0487: ArrayValue newArray = new ArrayValueImpl(array.getSize());
0488:
0489: for (Map.Entry<Value, Value> entry : array.entrySet()) {
0490: Value entryValue = entry.getValue();
0491: Value entryKey = entry.getKey();
0492:
0493: if (searchValue == null
0494: || searchValue instanceof DefaultValue)
0495: newArray.put(entryKey);
0496: else if (entryValue.eq(searchValue))
0497: newArray.put(entryKey);
0498: }
0499:
0500: return newArray;
0501: }
0502:
0503: /**
0504: * Returns an array with a number of indices filled with the given value,
0505: * starting at the start index.
0506: *
0507: * @param start the index to start filling the array
0508: * @param num the number of entries to fill
0509: * @param value the value to fill the entries with
0510: * @return an array filled with the given value starting from the given start
0511: * index
0512: */
0513: public Value array_fill(Env env, long start, long num, Value value) {
0514:
0515: if (num < 0) {
0516: env.warning("Number of elements must be positive");
0517:
0518: return BooleanValue.FALSE;
0519: }
0520:
0521: ArrayValue array = new ArrayValueImpl();
0522:
0523: for (long k = start; k < num + start; k++)
0524: array.put(LongValue.create(k), value);
0525:
0526: return array;
0527: }
0528:
0529: /**
0530: * Returns an array with the given array's keys as values and its values as
0531: * keys. If the given array has matching values, the latest value will be
0532: * transfered and the others will be lost.
0533: *
0534: * @param array the array to flip
0535: * @return an array with it's keys and values swapped
0536: */
0537: public Value array_flip(Env env, ArrayValue array) {
0538: if (array == null)
0539: return BooleanValue.FALSE;
0540:
0541: ArrayValue newArray = new ArrayValueImpl();
0542:
0543: for (Map.Entry<Value, Value> entry : array.entrySet()) {
0544: Value entryValue = entry.getValue();
0545:
0546: if ((entryValue.isLongConvertible())
0547: || (entryValue instanceof StringValue))
0548: newArray.put(entryValue, entry.getKey());
0549: else
0550: env.warning("Can only flip STRING and INTEGER values!");
0551: }
0552:
0553: return newArray;
0554: }
0555:
0556: /**
0557: * Returns an array with either the front/end padded with the pad value. If
0558: * the pad size is positive, the padding is performed on the end. If
0559: * negative, then the array is padded on the front. The pad size is the new
0560: * array size. If this size is not greater than the current array size, then
0561: * the original input array is returned.
0562: *
0563: * @param input the array to pad
0564: * @param padSize the amount to pad the array by
0565: * @param padValue determines front/back padding and the value to place in the
0566: * padded space
0567: * @return a padded array
0568: */
0569: public Value array_pad(Env env, ArrayValue input, long padSize,
0570: Value padValue) {
0571: if (input == null)
0572: return NullValue.NULL;
0573:
0574: long inputSize = input.getSize();
0575:
0576: long size = Math.abs(padSize);
0577:
0578: if (input.getSize() >= size)
0579: return input;
0580:
0581: if (size - inputSize > 1048576) {
0582: env
0583: .warning("You may only pad up to 1048576 elements at a time");
0584:
0585: return BooleanValue.FALSE;
0586: }
0587:
0588: ArrayValue paddedArray = new ArrayValueImpl();
0589:
0590: boolean padFront = padSize < 0;
0591:
0592: Iterator<Value> keyIterator = input.keySet().iterator();
0593:
0594: for (long ctr = 0; ctr < size; ctr++) {
0595: Value newValue;
0596:
0597: if (padFront && ctr < size - inputSize)
0598: newValue = padValue;
0599: else if ((!padFront) && ctr >= inputSize)
0600: newValue = padValue;
0601: else
0602: newValue = input.get(keyIterator.next());
0603:
0604: paddedArray.put(LongValue.create(ctr), newValue);
0605: }
0606:
0607: return paddedArray;
0608: }
0609:
0610: /**
0611: * Returns an array that filters out any values that do not hold true when
0612: * used in the callback function.
0613: *
0614: * @param array the array to filter
0615: * @param callback the function name for filtering
0616: * @return a filtered array
0617: */
0618: public Value array_filter(Env env, ArrayValue array, @Optional
0619: Callback callback) {
0620: if (array == null)
0621: return NullValue.NULL;
0622:
0623: ArrayValue filteredArray = new ArrayValueImpl();
0624:
0625: if (callback != null) {
0626:
0627: if (!callback.isValid()) {
0628: env.warning("The second argument, '"
0629: + ((CallbackFunction) callback)
0630: .getFunctionName()
0631: + "', should be a valid callback");
0632: return NullValue.NULL;
0633: }
0634:
0635: try {
0636: Iterator<Value> iter = array.getKeyIterator(env);
0637:
0638: while (iter.hasNext()) {
0639: Value key = iter.next();
0640: Value val = array.getRaw(key);
0641:
0642: boolean isMatch = callback.call(env, array, key,
0643: val).toBoolean();
0644:
0645: if (isMatch) {
0646: filteredArray.put(key, val);
0647: }
0648: }
0649: } catch (Throwable t) {
0650: log.log(Level.WARNING, t.toString(), t);
0651: env
0652: .warning("An error occurred while invoking the filter callback");
0653:
0654: return NullValue.NULL;
0655: }
0656: } else {
0657:
0658: for (Map.Entry<Value, Value> entry : array.entrySet()) {
0659: if (entry.getValue().toBoolean())
0660: filteredArray.put(entry.getKey(), entry.getValue());
0661: }
0662: }
0663:
0664: return filteredArray;
0665: }
0666:
0667: /**
0668: * Returns the product of the input array's elements as a double.
0669: *
0670: * @param array the array for who's product is to be found
0671: * @return the produce of the array's elements
0672: */
0673: public Value array_product(Env env, ArrayValue array) {
0674: if (array == null)
0675: return NullValue.NULL;
0676:
0677: if (array.getSize() == 0)
0678: return DoubleValue.create(0);
0679:
0680: double product = 1;
0681:
0682: for (Map.Entry<Value, Value> entry : array.entrySet())
0683: product *= entry.getValue().toDouble();
0684:
0685: return DoubleValue.create(product);
0686: }
0687:
0688: /**
0689: * Appends a value to the array
0690: *
0691: * @return the number of elements in the final array
0692: */
0693: public int array_push(Env env, ArrayValue array, Value[] values) {
0694: for (Value value : values) {
0695: array.put(value);
0696: }
0697:
0698: return array.getSize();
0699: }
0700:
0701: /**
0702: * Returns num sized array of random keys from the given array
0703: *
0704: * @param array the array from which the keys will come from
0705: * @param num the number of random keys to return
0706: * @return the produce of the array's elements
0707: */
0708: public Value array_rand(Env env, ArrayValue array, @Optional("1")
0709: long num) {
0710: if (array == null)
0711: return NullValue.NULL;
0712:
0713: if (array.getSize() == 0)
0714: return NullValue.NULL;
0715:
0716: if (num < 1 || array.getSize() < num) {
0717: env
0718: .warning("Second argument has to be between 1 and the number of "
0719: + "elements in the array");
0720:
0721: return NullValue.NULL;
0722: }
0723:
0724: long arraySize = array.getSize();
0725:
0726: Value[] keys = new Value[(int) arraySize];
0727:
0728: array.keySet().toArray(keys);
0729:
0730: if (num == 1) {
0731: int index = (int) (RandomUtil.getRandomLong() % arraySize);
0732:
0733: if (index < 0)
0734: index *= -1;
0735:
0736: return keys[index];
0737: }
0738:
0739: int length = keys.length;
0740: for (int i = 0; i < length; i++) {
0741: int rand = RandomUtil.nextInt(length);
0742:
0743: Value temp = keys[rand];
0744: keys[rand] = keys[i];
0745: keys[i] = temp;
0746: }
0747:
0748: ArrayValue randArray = new ArrayValueImpl();
0749:
0750: for (int i = 0; i < num; i++) {
0751: randArray.put(keys[i]);
0752: }
0753:
0754: return randArray;
0755: }
0756:
0757: /**
0758: * Returns the value of the array when its elements have been reduced using
0759: * the callback function.
0760: *
0761: * @param array the array to reduce
0762: * @param callback the function to use for reducing the array
0763: * @param initialValue used as the element before the first element of the
0764: * array for purposes of using the callback function
0765: * @return the result from reducing the input array with the callback
0766: * function
0767: */
0768: public Value array_reduce(Env env, ArrayValue array,
0769: Callback callback, @Optional("NULL")
0770: Value initialValue) {
0771: if (array == null)
0772: return NullValue.NULL;
0773:
0774: if (callback == null || !callback.isValid()) {
0775: env.warning("The second argument, '" + callback
0776: + "', should be a valid callback");
0777:
0778: return NullValue.NULL;
0779: }
0780:
0781: Value result = initialValue;
0782:
0783: for (Map.Entry<Value, Value> entry : array.entrySet()) {
0784: try {
0785: // XXX: will this callback modify the array?
0786: result = callback.call(env, result, entry.getValue());
0787: } catch (Throwable t) {
0788: // XXX: may be used for error checking later
0789: log.log(Level.WARNING, t.toString(), t);
0790: env
0791: .warning("An error occurred while invoking the reduction callback");
0792:
0793: return NullValue.NULL;
0794: }
0795: }
0796:
0797: return result;
0798: }
0799:
0800: /**
0801: * Returns the inputted array reversed, preserving the keys if keyed is true
0802: *
0803: * @param inputArray the array to reverse
0804: * @param keyed true if the keys are to be preservered
0805: * @return the array in reverse
0806: */
0807: public Value array_reverse(Env env, ArrayValue inputArray,
0808: @Optional("false")
0809: boolean keyed) {
0810: if (inputArray == null)
0811: return NullValue.NULL;
0812:
0813: Map.Entry<Value, Value>[] entryArray = new Map.Entry[inputArray
0814: .getSize()];
0815:
0816: inputArray.entrySet().toArray(entryArray);
0817:
0818: ArrayValue newArray = new ArrayValueImpl();
0819:
0820: int newIndex = 0;
0821:
0822: for (int index = entryArray.length - 1; index > -1; index--) {
0823: Value currentKey = entryArray[index].getKey();
0824:
0825: Value currentValue = entryArray[index].getValue();
0826:
0827: if (keyed || (currentKey instanceof StringValue))
0828: newArray.put(currentKey, currentValue);
0829: else {
0830: newArray.put(LongValue.create(newIndex), currentValue);
0831:
0832: newIndex++;
0833: }
0834: }
0835:
0836: return newArray;
0837: }
0838:
0839: /**
0840: * Returns the key of the needle being searched for or false if it's not
0841: * found
0842: *
0843: * @param needle the value to search for
0844: * @param array the array to search
0845: * @param strict checks for type aswell
0846: * @return the key of the needle
0847: */
0848: public Value array_search(Env env, @ReadOnly
0849: Value needle, @ReadOnly
0850: ArrayValue array, @Optional("false")
0851: boolean strict) {
0852: // php/171i
0853: // php/172y
0854:
0855: if (array == null)
0856: return BooleanValue.FALSE;
0857:
0858: Iterator<Map.Entry<Value, Value>> iterator = array
0859: .getIterator(env);
0860:
0861: while (iterator.hasNext()) {
0862: Map.Entry<Value, Value> entry = iterator.next();
0863:
0864: Value entryValue = entry.getValue();
0865: Value entryKey = entry.getKey();
0866:
0867: if (needle.eq(entryValue)) {
0868: if (strict) {
0869: if ((entryValue.getType()).equals(needle.getType()))
0870: return entryKey;
0871: } else
0872: return entryKey;
0873: }
0874: }
0875:
0876: return BooleanValue.FALSE;
0877: }
0878:
0879: /**
0880: * Shifts the elements in the array left by one, returning the leftmost value
0881: *
0882: * @param array the array to shift
0883: * @return the left most value in the array
0884: */
0885: public Value array_shift(Env env, ArrayValue array) {
0886: if (array == null)
0887: return NullValue.NULL;
0888:
0889: if (array.getSize() < 1)
0890: return NullValue.NULL;
0891:
0892: Iterator<Value> iterator = array.keySet().iterator();
0893:
0894: Value firstValue = array.remove(iterator.next());
0895:
0896: array.keyReset(0, NOT_STRICT);
0897:
0898: return firstValue;
0899: }
0900:
0901: /**
0902: * Returns a chunk of the array. The offset is the start index, elements is
0903: * the number of values to take, and presKeys is if the keys are to be
0904: * preserved. If offset is negative, then it's that number from the end of the
0905: * array. If elements is negative, then the new array will have from offset
0906: * to elements number of values.
0907: *
0908: * @param array the array to take the chunk from
0909: * @param offset the start index for the new array chunk
0910: * @param elements the number of elements in the array chunk
0911: * @param presKeys true if the keys of the elements are to be preserved, false
0912: * otherwise
0913: * @return the array chunk
0914: */
0915: public Value array_slice(Env env, ArrayValue array, long offset,
0916: @Optional("NULL")
0917: Value elements, @Optional("false")
0918: boolean presKeys) {
0919: if (array == null)
0920: return NullValue.NULL;
0921:
0922: long size = array.getSize();
0923:
0924: long startIndex = offset;
0925:
0926: if (offset < 0)
0927: startIndex = size + offset;
0928:
0929: long endIndex = size;
0930:
0931: if (elements != NullValue.NULL) {
0932: endIndex = elements.toLong();
0933:
0934: if (endIndex < 0)
0935: endIndex += size;
0936: else
0937: endIndex += startIndex;
0938: }
0939:
0940: Iterator<Map.Entry<Value, Value>> iterator = array.entrySet()
0941: .iterator();
0942:
0943: ArrayValue slicedArray = new ArrayValueImpl();
0944:
0945: for (int k = 0; k < endIndex && iterator.hasNext(); k++) {
0946: Map.Entry<Value, Value> entry = iterator.next();
0947:
0948: if (k >= startIndex) {
0949: Value entryKey = entry.getKey();
0950:
0951: Value entryValue = entry.getValue();
0952:
0953: if ((entryKey instanceof StringValue) || presKeys)
0954: slicedArray.put(entryKey, entryValue);
0955: else
0956: slicedArray.put(entryValue);
0957: }
0958: }
0959:
0960: return slicedArray;
0961: }
0962:
0963: /**
0964: * Returns the removed chunk of the arrayV and splices in replace. If offset
0965: * is negative, then the start index is that far from the end. Otherwise, it
0966: * is the start index. If length is not given then from start index to the
0967: * end is removed. If length is negative, that is the index to stop removing
0968: * elements. Otherwise that is the number of elements to remove. If replace
0969: * is given, replace will be inserted into the arrayV at offset.
0970: *
0971: * @param array the arrayV to splice
0972: * @param offset the start index for the new arrayV chunk
0973: * @param length the number of elements to remove / stop index
0974: * @param replace the elements to add to the arrayV
0975: * @return the part of the arrayV removed from input
0976: */
0977: public Value array_splice(Env env, @Reference
0978: Value arrayVar, //array gets spliced at offset
0979: int offset, @Optional("NULL")
0980: Value length, @Optional
0981: Value replace) {
0982: if (!arrayVar.isset())
0983: return NullValue.NULL;
0984:
0985: ArrayValue array = arrayVar.toArrayValue(env);
0986:
0987: if (array == null)
0988: return NullValue.NULL;
0989:
0990: int size = array.getSize();
0991:
0992: int startIndex = offset;
0993:
0994: if (startIndex < 0)
0995: startIndex += size;
0996:
0997: int endIndex = size;
0998:
0999: if (length != NullValue.NULL) {
1000: endIndex = length.toInt();
1001:
1002: if (endIndex < 0)
1003: endIndex += size;
1004: else
1005: endIndex += startIndex;
1006: }
1007:
1008: return spliceImpl(arrayVar, array, startIndex, endIndex,
1009: (ArrayValue) replace.toArray());
1010: }
1011:
1012: public Value spliceImpl(Value var, ArrayValue array, int start,
1013: int end, ArrayValue replace) {
1014: int index = 0;
1015:
1016: ArrayValue newArray = new ArrayValueImpl();
1017: ArrayValue result = new ArrayValueImpl();
1018:
1019: var.set(newArray);
1020:
1021: ArrayValue.Entry ptr = array.getHead();
1022: for (; ptr != null; ptr = ptr.getNext()) {
1023: Value key = ptr.getKey();
1024:
1025: if (start == index && replace != null) {
1026: for (ArrayValue.Entry replaceEntry = replace.getHead(); replaceEntry != null; replaceEntry = replaceEntry
1027: .getNext()) {
1028: newArray.put(replaceEntry.getValue());
1029: }
1030: }
1031:
1032: if (start <= index && index < end) {
1033: if (ptr.getKey() instanceof StringValue)
1034: result.put(ptr.getKey(), ptr.getValue());
1035: else
1036: result.put(ptr.getValue());
1037: } else {
1038: if (ptr.getKey() instanceof StringValue)
1039: newArray.put(ptr.getKey(), ptr.getValue());
1040: else
1041: newArray.put(ptr.getValue());
1042: }
1043:
1044: index++;
1045: }
1046:
1047: if (index <= start && replace != null) {
1048: for (ArrayValue.Entry replaceEntry = replace.getHead(); replaceEntry != null; replaceEntry = replaceEntry
1049: .getNext()) {
1050: newArray.put(replaceEntry.getValue());
1051: }
1052: }
1053:
1054: return result;
1055: }
1056:
1057: /**
1058: * Returns the sum of the elements in the array
1059: *
1060: * @param array the array to sum
1061: * @return the sum of the elements
1062: */
1063: public Value array_sum(Env env, @ReadOnly
1064: ArrayValue array) {
1065: if (array == null)
1066: return NullValue.NULL;
1067:
1068: double sum = 0;
1069:
1070: for (Map.Entry<Value, Value> entry : array.entrySet())
1071: sum += entry.getValue().toDouble();
1072:
1073: return DoubleValue.create(sum);
1074: }
1075:
1076: // XXX: array_udiff
1077: // XXX: array_udiff_assoc
1078: // XXX: array_udiff_uassoc
1079:
1080: // XXX: array_uintersect
1081: // XXX: array_uintersect_assoc
1082: // XXX: array_uintersect_uassoc
1083:
1084: /**
1085: * Returns the inputted array without duplicates
1086: *
1087: * @param array the array to get rid of the duplicates from
1088: * @return an array without duplicates
1089: */
1090: public Value array_unique(Env env, ArrayValue array) {
1091: if (array == null)
1092: return BooleanValue.FALSE;
1093:
1094: array.sort(CNO_VALUE_NORMAL, NO_KEY_RESET, NOT_STRICT);
1095:
1096: Map.Entry<Value, Value> lastEntry = null;
1097:
1098: ArrayValue uniqueArray = new ArrayValueImpl();
1099:
1100: for (Map.Entry<Value, Value> entry : array.entrySet()) {
1101: Value entryValue = entry.getValue();
1102:
1103: if (lastEntry == null) {
1104: uniqueArray.put(entry.getKey(), entryValue);
1105:
1106: lastEntry = entry;
1107:
1108: continue;
1109: }
1110:
1111: Value lastEntryValue = lastEntry.getValue();
1112:
1113: if (!entryValue.toString()
1114: .equals(lastEntryValue.toString()))
1115: uniqueArray.put(entry.getKey(), entryValue);
1116:
1117: lastEntry = entry;
1118: }
1119:
1120: uniqueArray.sort(CNO_KEY_NORMAL, NO_KEY_RESET, NOT_STRICT);
1121:
1122: return uniqueArray;
1123: }
1124:
1125: /**
1126: * Prepends the elements to the array
1127: *
1128: * @param array the array to shift
1129: * @param values
1130: * @return the left most value in the array
1131: */
1132: public Value array_unshift(Env env, ArrayValue array, Value[] values) {
1133: if (array == null)
1134: return NullValue.NULL;
1135:
1136: for (int i = values.length - 1; i >= 0; i--) {
1137: array.unshift(values[i]);
1138: }
1139:
1140: array.keyReset(0, NOT_STRICT);
1141:
1142: return array;
1143: }
1144:
1145: /**
1146: * Returns the values in the passed array with numerical indices.
1147: *
1148: * @param array the array to get the values from
1149: * @return an array with the values of the passed array
1150: */
1151: public Value array_values(Env env, ArrayValue array) {
1152: if (array == null)
1153: return NullValue.NULL;
1154:
1155: ArrayValue arrayValues = new ArrayValueImpl();
1156:
1157: for (Map.Entry<Value, Value> entry : array.entrySet())
1158: arrayValues.put(entry.getValue());
1159:
1160: return arrayValues;
1161: }
1162:
1163: /**
1164: * Executes a callback on each of the elements in the array.
1165: *
1166: * @param array the array to walk along
1167: * @param callback the callback function
1168: * @param userData extra parameter required by the callback function
1169: *
1170: * @return true if the walk succedded, false otherwise
1171: */
1172: public boolean array_walk(Env env, @NotNull
1173: ArrayValue array, Callback callback, @Optional("NULL")
1174: Value userData) {
1175: if (callback == null || !callback.isValid()) {
1176: env.error(L.l("'{0}' is an unknown function.", callback
1177: .getCallbackName()));
1178: return false;
1179: }
1180:
1181: if (array == null)
1182: return false;
1183:
1184: try {
1185: Value[] keyArray = array.getKeyArray(env);
1186:
1187: for (int i = 0; i < keyArray.length; i++) {
1188: Value key = keyArray[i];
1189: Value val = array.getRaw(key);
1190:
1191: callback.call(env, array, key, val, key, userData);
1192: }
1193:
1194: return true;
1195: } catch (Exception e) {
1196: log.log(Level.WARNING, e.toString(), e);
1197: env.warning("An error occured while invoking the callback",
1198: e);
1199:
1200: return false;
1201: }
1202: }
1203:
1204: /**
1205: * Recursively executes a callback function on all elements in the array,
1206: * including elements of elements (i.e., arrays within arrays). Returns true
1207: * if the process succeeded, otherwise false.
1208: *
1209: * @param array the array to walk
1210: * @param call the name of the callback function
1211: * @param extra extra parameter required by the callback function
1212: * @return true if the walk succedded, false otherwise
1213: */
1214: public boolean array_walk_recursive(Env env, @NotNull
1215: ArrayValue array, Callback callback, @Optional("NULL")
1216: Value extra) {
1217: if (callback == null || !callback.isValid()) {
1218: env.error(L.l("'{0}' is an unknown function.", callback
1219: .getCallbackName()));
1220: return false;
1221: }
1222:
1223: if (array == null)
1224: return false;
1225:
1226: try {
1227: Value[] keyArray = array.getKeyArray(env);
1228:
1229: for (int i = 0; i < keyArray.length; i++) {
1230: Value key = keyArray[i];
1231: Value val = array.getRaw(key);
1232:
1233: if (val.isArray()) {
1234: boolean result = array_walk_recursive(env,
1235: (ArrayValue) val.toValue(), callback, extra);
1236:
1237: if (!result)
1238: return false;
1239: } else {
1240: callback.call(env, array, key, val, key, extra);
1241: }
1242: }
1243:
1244: return true;
1245: } catch (Exception e) {
1246: log.log(Level.WARNING, e.toString(), e);
1247: env.warning("An error occured while invoking the callback",
1248: e);
1249:
1250: return false;
1251: }
1252: }
1253:
1254: /**
1255: * Sorts the array based on values in reverse order, preserving keys
1256: *
1257: * @param array the array to sort
1258: * @param sortFlag provides optional methods to process the sort
1259: * @return true if the sort works, false otherwise
1260: * @throws ClassCastException if the elements are not mutually comparable
1261: */
1262: public boolean arsort(Env env, ArrayValue array, @Optional
1263: long sortFlag) {
1264: if (array == null)
1265: return false;
1266:
1267: switch ((int) sortFlag) {
1268: case SORT_STRING:
1269: array.sort(CS_VALUE_REVERSE, NO_KEY_RESET, NOT_STRICT);
1270: break;
1271: case SORT_NUMERIC:
1272: array.sort(CN_VALUE_REVERSE, NO_KEY_RESET, NOT_STRICT);
1273: break;
1274: case SORT_LOCALE_STRING:
1275: Locale locale = env.getLocaleInfo().getCollate();
1276: array.sort(new CompareLocale(ArrayValue.GET_VALUE,
1277: SORT_REVERSE, Collator.getInstance(locale)),
1278: NO_KEY_RESET, NOT_STRICT);
1279: break;
1280: default:
1281: array.sort(CNO_VALUE_REVERSE, NO_KEY_RESET, NOT_STRICT);
1282: break;
1283: }
1284:
1285: return true;
1286: }
1287:
1288: /**
1289: * Sorts the array based on values in ascending order, preserving keys
1290: *
1291: * @param array the array to sort
1292: * @param sortFlag provides optional methods to process the sort
1293: * @return true if the sort works, false otherwise
1294: * @throws ClassCastException if the elements are not mutually comparable
1295: */
1296: static public boolean asort(Env env, ArrayValue array, @Optional
1297: long sortFlag) {
1298: if (array == null)
1299: return false;
1300:
1301: switch ((int) sortFlag) {
1302: case SORT_STRING:
1303: array.sort(CS_VALUE_NORMAL, NO_KEY_RESET, NOT_STRICT);
1304: break;
1305: case SORT_NUMERIC:
1306: array.sort(CN_VALUE_NORMAL, NO_KEY_RESET, NOT_STRICT);
1307: break;
1308: case SORT_LOCALE_STRING:
1309: Locale locale = env.getLocaleInfo().getCollate();
1310: array.sort(new CompareLocale(ArrayValue.GET_VALUE,
1311: SORT_NORMAL, Collator.getInstance(locale)),
1312: NO_KEY_RESET, NOT_STRICT);
1313: break;
1314: default:
1315: array.sort(CNO_VALUE_NORMAL, NO_KEY_RESET, NOT_STRICT);
1316: break;
1317: }
1318:
1319: return true;
1320: }
1321:
1322: /**
1323: * Sorts the array based on keys in ascending order, preserving keys
1324: *
1325: * @param array the array to sort
1326: * @param sortFlag provides optional methods to process the sort
1327: * @return true if the sort works, false otherwise
1328: * @throws ClassCastException if the elements are not mutually comparable
1329: */
1330: static public boolean ksort(Env env, ArrayValue array, @Optional
1331: long sortFlag) {
1332: if (array == null)
1333: return false;
1334:
1335: switch ((int) sortFlag) {
1336: case SORT_STRING:
1337: array.sort(CS_KEY_NORMAL, NO_KEY_RESET, NOT_STRICT);
1338: break;
1339: case SORT_NUMERIC:
1340: array.sort(CN_KEY_NORMAL, NO_KEY_RESET, NOT_STRICT);
1341: break;
1342: case SORT_LOCALE_STRING:
1343: Locale locale = env.getLocaleInfo().getCollate();
1344: array.sort(new CompareLocale(ArrayValue.GET_KEY,
1345: SORT_NORMAL, Collator.getInstance(locale)),
1346: NO_KEY_RESET, NOT_STRICT);
1347: break;
1348: default:
1349: array.sort(CNO_KEY_NORMAL, NO_KEY_RESET, NOT_STRICT);
1350: break;
1351: }
1352:
1353: return true;
1354: }
1355:
1356: /**
1357: * Sorts the array based on keys in reverse order, preserving keys
1358: *
1359: * @param array the array to sort
1360: * @param sortFlag provides optional methods to process the sort
1361: * @return true if the sort works, false otherwise
1362: * @throws ClassCastException if the elements are not mutually comparable
1363: */
1364: public boolean krsort(Env env, ArrayValue array, @Optional
1365: long sortFlag) {
1366: if (array == null)
1367: return false;
1368:
1369: switch ((int) sortFlag) {
1370: case SORT_STRING:
1371: array.sort(CS_KEY_REVERSE, NO_KEY_RESET, NOT_STRICT);
1372: break;
1373: case SORT_NUMERIC:
1374: array.sort(CN_KEY_REVERSE, NO_KEY_RESET, NOT_STRICT);
1375: break;
1376: case SORT_LOCALE_STRING:
1377: Locale locale = env.getLocaleInfo().getCollate();
1378: array.sort(new CompareLocale(ArrayValue.GET_KEY,
1379: SORT_REVERSE, Collator.getInstance(locale)),
1380: NO_KEY_RESET, NOT_STRICT);
1381: break;
1382: default:
1383: array.sort(CNO_KEY_REVERSE, NO_KEY_RESET, NOT_STRICT);
1384: break;
1385: }
1386:
1387: return true;
1388: }
1389:
1390: /**
1391: * Sorts the array based on string values using natural order, preserving
1392: * keys, case sensitive
1393: *
1394: * @param array the array to sort
1395: * @return true if the sort works, false otherwise
1396: * @throws ClassCastException if the elements are not mutually comparable
1397: */
1398: static public Value natsort(ArrayValue array) {
1399: if (array == null)
1400: return NullValue.NULL;
1401:
1402: trimArrayStrings(array);
1403:
1404: array
1405: .sort(CNA_VALUE_NORMAL_SENSITIVE, NO_KEY_RESET,
1406: NOT_STRICT);
1407:
1408: return BooleanValue.TRUE;
1409: }
1410:
1411: /**
1412: * Sorts the array based on string values using natural order, preserving
1413: * keys, case insensitive
1414: *
1415: * @param array the array to sort
1416: * @return true if the sort works, false otherwise
1417: * @throws ClassCastException if the elements are not mutually comparable
1418: */
1419: static public Value natcasesort(ArrayValue array) {
1420: if (array == null)
1421: return NullValue.NULL;
1422:
1423: trimArrayStrings(array);
1424:
1425: array.sort(CNA_VALUE_NORMAL_INSENSITIVE, NO_KEY_RESET,
1426: NOT_STRICT);
1427:
1428: return BooleanValue.TRUE;
1429: }
1430:
1431: /**
1432: * Helper function for natsort and natcasesort to trim the string in the
1433: * array
1434: *
1435: * @param array the array to trim strings from
1436: */
1437: static private void trimArrayStrings(ArrayValue array) {
1438: if (array != null) {
1439:
1440: for (Map.Entry<Value, Value> entry : array.entrySet()) {
1441: Value entryValue = entry.getValue();
1442:
1443: if (entryValue instanceof StringValue)
1444: array.put(entry.getKey(), StringValue
1445: .create(entryValue.toString().trim()));
1446: }
1447: }
1448: }
1449:
1450: // XXX: compact
1451:
1452: /**
1453: * Determines if the key is in the array
1454: *
1455: * @param needle the array to sort
1456: * @return true if the sort works, false otherwise
1457: * @throws ClassCastException if the elements are not mutually comparable
1458: */
1459: public boolean in_array(@ReadOnly
1460: Value needle, @ReadOnly
1461: ArrayValue stack, @Optional("false")
1462: boolean strict) {
1463: if (stack == null)
1464: return false;
1465:
1466: if (strict)
1467: return stack.containsStrict(needle) != NullValue.NULL;
1468: else
1469: return stack.contains(needle) != NullValue.NULL;
1470: }
1471:
1472: /**
1473: * Sorts the array based on values in ascending order
1474: *
1475: * @param array the array to sort
1476: * @param sortFlag provides optional methods to process the sort
1477: * @return true if the sort works, false otherwise
1478: * @throws ClassCastException if the elements are not mutually comparable
1479: */
1480: public boolean sort(Env env, ArrayValue array, @Optional
1481: long sortFlag) {
1482: if (array == null)
1483: return false;
1484:
1485: switch ((int) sortFlag) {
1486: case SORT_STRING:
1487: array.sort(CS_VALUE_NORMAL, KEY_RESET, STRICT);
1488: break;
1489: case SORT_NUMERIC:
1490: array.sort(CN_VALUE_NORMAL, KEY_RESET, STRICT);
1491: break;
1492: case SORT_LOCALE_STRING:
1493: Locale locale = env.getLocaleInfo().getCollate();
1494: array.sort(new CompareLocale(ArrayValue.GET_VALUE,
1495: SORT_NORMAL, Collator.getInstance(locale)),
1496: KEY_RESET, STRICT);
1497: break;
1498: default:
1499: array.sort(CNO_VALUE_NORMAL, KEY_RESET, STRICT);
1500: break;
1501: }
1502:
1503: return true;
1504: }
1505:
1506: /**
1507: * Sorts the array based on values in reverse order
1508: *
1509: * @param array the array to sort
1510: * @param sortFlag provides optional methods to process the sort
1511: * @return true if the sort works, false otherwise
1512: * @throws ClassCastException if the elements are not mutually comparable
1513: */
1514: public boolean rsort(Env env, ArrayValue array, @Optional
1515: long sortFlag) {
1516: if (array == null)
1517: return false;
1518:
1519: switch ((int) sortFlag) {
1520: case SORT_STRING:
1521: array.sort(CS_VALUE_REVERSE, KEY_RESET, STRICT);
1522: break;
1523: case SORT_NUMERIC:
1524: array.sort(CN_VALUE_REVERSE, KEY_RESET, STRICT);
1525: break;
1526: case SORT_LOCALE_STRING:
1527: Locale locale = env.getLocaleInfo().getCollate();
1528: array.sort(new CompareLocale(ArrayValue.GET_VALUE,
1529: SORT_REVERSE, Collator.getInstance(locale)),
1530: KEY_RESET, STRICT);
1531: break;
1532: default:
1533: array.sort(CNO_VALUE_REVERSE, KEY_RESET, STRICT);
1534: break;
1535: }
1536:
1537: return true;
1538: }
1539:
1540: /**
1541: * Sorts the array based on values in ascending order using a callback
1542: * function
1543: *
1544: * @param array the array to sort
1545: * @param func the name of the callback function
1546: * @param sortFlag provides optional methods to process the sort
1547: * @return true if the sort works, false otherwise
1548: * @throws ClassCastException if the elements are not mutually comparable
1549: */
1550: public boolean usort(Env env, ArrayValue array, Callback func,
1551: @Optional
1552: long sortFlag) {
1553: if (array == null)
1554: return false;
1555:
1556: if (!func.isValid()) {
1557: env.warning(L.l("Invalid comparison function"));
1558: return false;
1559: }
1560:
1561: CompareCallBack cmp;
1562:
1563: // XXX: callback needs to be able to modify array?
1564: cmp = new CompareCallBack(ArrayValue.GET_VALUE, SORT_NORMAL,
1565: func, env);
1566:
1567: array.sort(cmp, KEY_RESET, STRICT);
1568:
1569: return true;
1570: }
1571:
1572: /**
1573: * Sorts the array based on values in ascending order using a callback
1574: * function
1575: *
1576: * @param array the array to sort
1577: * @param func the name of the callback function
1578: * @param sortFlag provides optional methods to process the sort
1579: * @return true if the sort works, false otherwise
1580: * @throws ClassCastException if the elements are not mutually comparable
1581: */
1582: static public boolean uasort(Env env, ArrayValue array,
1583: Callback func, @Optional
1584: long sortFlag) {
1585: if (array == null)
1586: return false;
1587:
1588: if (!func.isValid()) {
1589: env.warning(L.l("Invalid comparison function"));
1590: return false;
1591: }
1592:
1593: // XXX: callback needs to be able to modify array?
1594: array.sort(new CompareCallBack(ArrayValue.GET_VALUE,
1595: SORT_NORMAL, func, env), NO_KEY_RESET, NOT_STRICT);
1596:
1597: return true;
1598: }
1599:
1600: /**
1601: * Sorts the array based on values in ascending order using a callback
1602: * function
1603: *
1604: * @param array the array to sort
1605: * @param func the name of the callback function
1606: * @param sortFlag provides optional methods to process the sort
1607: * @return true if the sort works, false otherwise
1608: * @throws ClassCastException if the elements are not mutually comparable
1609: */
1610: static public boolean uksort(Env env, ArrayValue array,
1611: Callback func, @Optional
1612: long sortFlag) {
1613: if (array == null)
1614: return false;
1615:
1616: if (!func.isValid()) {
1617: env.warning(L.l("Invalid comparison function"));
1618: return false;
1619: }
1620:
1621: CompareCallBack cmp;
1622:
1623: // XXX: callback needs to be able to modify array?
1624: cmp = new CompareCallBack(ArrayValue.GET_KEY, SORT_NORMAL,
1625: func, env);
1626:
1627: array.sort(cmp, NO_KEY_RESET, NOT_STRICT);
1628:
1629: return true;
1630: }
1631:
1632: /**
1633: * Creates an array using the start and end values provided
1634: *
1635: * @param start the 0 index element
1636: * @param end the length - 1 index element
1637: * @param step the new value is increased by this to determine the value for
1638: * the next element
1639: * @return the new array
1640: */
1641: public Value range(Env env, @ReadOnly
1642: Value start, @ReadOnly
1643: Value end, @Optional("1")
1644: long step) {
1645: if (step < 1)
1646: step = 1;
1647:
1648: if (!start.getType().equals(end.getType())) {
1649: start = LongValue.create(start.toLong());
1650: end = LongValue.create(end.toLong());
1651: } else if (Character.isDigit(start.toChar())) {
1652: start = LongValue.create(start.toLong());
1653: end = LongValue.create(end.toLong());
1654: } else {
1655: start = rangeIncrement(start, 0);
1656: end = rangeIncrement(end, 0);
1657: }
1658:
1659: if (start.eq(end)) {
1660: } else if (start instanceof StringValue
1661: && (Math.abs(end.toChar() - start.toChar()) < step)) {
1662: env.warning("steps exceeds the specified range");
1663:
1664: return BooleanValue.FALSE;
1665: } else if (start instanceof LongValue
1666: && (Math.abs(end.toLong() - start.toLong()) < step)) {
1667: env.warning("steps exceeds the specified range");
1668:
1669: return BooleanValue.FALSE;
1670: }
1671:
1672: boolean increment = true;
1673:
1674: if (!end.geq(start)) {
1675: step *= -1;
1676: increment = false;
1677: }
1678:
1679: ArrayValue array = new ArrayValueImpl();
1680:
1681: do {
1682: array.put(start);
1683:
1684: start = rangeIncrement(start, step);
1685: } while ((increment && start.leq(end))
1686: || (!increment && start.geq(end)));
1687:
1688: return array;
1689: }
1690:
1691: private Value rangeIncrement(Value value, long step) {
1692: if (value instanceof StringValue)
1693: return StringValue.create((char) (value.toChar() + step));
1694:
1695: return LongValue.create(value.toLong() + step);
1696: }
1697:
1698: // XXX:You'll need to mark the function as XXX:, because I need to add an
1699: // attribute like @ModifiedSymbolTable and change some analysis of the
1700: // compilation based on that attribute.
1701: //
1702: // Basically, the compiled mode uses Java variables to store PHP
1703: // variables. The extract() call messes that up, or at least forces the
1704: // compiler to synchronize its view of the variables.
1705: // (email Re:extract: symbol table)
1706:
1707: /**
1708: * Inputs new variables into the symbol table from the passed array
1709: *
1710: * @param array the array contained the new variables
1711: * @param rawType flag to determine how to handle collisions
1712: * @param valuePrefix used along with the flag
1713: * @return the number of new variables added from the array to the symbol
1714: * table
1715: */
1716: @UsesSymbolTable
1717: public static Value extract(Env env, ArrayValue array,
1718: @Optional("EXTR_OVERWRITE")
1719: long rawType, @Optional("NULL")
1720: Value valuePrefix) {
1721: if (array == null)
1722: return NullValue.NULL;
1723:
1724: long extractType = rawType & ~EXTR_REFS;
1725:
1726: boolean extrRefs = (rawType & EXTR_REFS) != 0;
1727:
1728: if (extractType < EXTR_OVERWRITE
1729: || extractType > EXTR_IF_EXISTS
1730: && extractType != EXTR_REFS) {
1731: env.warning("Unknown extract type");
1732:
1733: return NullValue.NULL;
1734: }
1735:
1736: if (extractType >= EXTR_PREFIX_SAME
1737: && extractType <= EXTR_PREFIX_IF_EXISTS
1738: && (valuePrefix == null || (!(valuePrefix instanceof StringValue)))) {
1739: env.warning("Prefix expected to be specified");
1740:
1741: return NullValue.NULL;
1742: }
1743:
1744: String prefix = "";
1745:
1746: if (valuePrefix instanceof StringValue)
1747: prefix = valuePrefix.toString() + "_";
1748:
1749: int completedSymbols = 0;
1750:
1751: for (Value entryKey : array.keySet()) {
1752: Value entryValue;
1753:
1754: if (extrRefs)
1755: entryValue = array.getRef(entryKey);
1756: else
1757: entryValue = array.get(entryKey);
1758:
1759: String symbolName = entryKey.toString();
1760:
1761: Value tableValue = env.getValue(symbolName);
1762:
1763: switch ((int) extractType) {
1764: case EXTR_SKIP:
1765: if (tableValue != NullValue.NULL)
1766: symbolName = "";
1767:
1768: break;
1769: case EXTR_PREFIX_SAME:
1770: if (tableValue != NullValue.NULL)
1771: symbolName = prefix + symbolName;
1772:
1773: break;
1774: case EXTR_PREFIX_ALL:
1775: symbolName = prefix + symbolName;
1776:
1777: break;
1778: case EXTR_PREFIX_INVALID:
1779: if (!validVariableName(symbolName))
1780: symbolName = prefix + symbolName;
1781:
1782: break;
1783: case EXTR_IF_EXISTS:
1784: if (tableValue == NullValue.NULL)
1785: symbolName = "";//entryValue = tableValue;
1786:
1787: break;
1788: case EXTR_PREFIX_IF_EXISTS:
1789: if (tableValue != NullValue.NULL)
1790: symbolName = prefix + symbolName;
1791: else
1792: symbolName = "";
1793:
1794: break;
1795: default:
1796:
1797: break;
1798: }
1799:
1800: if (validVariableName(symbolName)) {
1801: env.setValue(symbolName, entryValue);
1802:
1803: completedSymbols++;
1804: }
1805: }
1806:
1807: return LongValue.create(completedSymbols);
1808: }
1809:
1810: /**
1811: * Helper function for extract to determine if a variable name is valid
1812: *
1813: * @param variableName the name to check
1814: * @return true if the name is valid, false otherwise
1815: */
1816: private static boolean validVariableName(String variableName) {
1817: if (variableName.length() < 1)
1818: return false;
1819:
1820: char checkChar = variableName.charAt(0);
1821:
1822: if (!Character.isLetter(checkChar) && checkChar != '_')
1823: return false;
1824:
1825: for (int k = 1; k < variableName.length(); k++) {
1826: checkChar = variableName.charAt(k);
1827:
1828: if (!Character.isLetterOrDigit(checkChar)
1829: && checkChar != '_')
1830: return false;
1831: }
1832:
1833: return true;
1834: }
1835:
1836: /**
1837: * Returns an array with everything that is in array and not in the other
1838: * arrays using a passed callback function for comparing
1839: *
1840: * @param array the primary array
1841: * @param arrays the vector of arrays to check the primary array's values
1842: * against
1843: * @return an array with all of the values that are in the primary array but
1844: * not in the other arrays
1845: */
1846: public Value array_diff(Env env, ArrayValue array, Value[] arrays) {
1847: if (array == null)
1848: return NullValue.NULL;
1849:
1850: if (arrays.length < 1) {
1851: env.warning("Wrong parameter count for array_diff()");
1852:
1853: return NullValue.NULL;
1854: }
1855:
1856: ArrayValue diffArray = new ArrayValueImpl();
1857:
1858: boolean valueFound;
1859:
1860: for (Map.Entry<Value, Value> entry : array.entrySet()) {
1861: valueFound = false;
1862:
1863: Value entryValue = entry.getValue();
1864:
1865: for (int k = 0; k < arrays.length && !valueFound; k++) {
1866: if (!(arrays[k] instanceof ArrayValue)) {
1867: env.warning("Argument #" + (k + 2)
1868: + " is not an array");
1869:
1870: return NullValue.NULL;
1871: }
1872:
1873: valueFound = ((ArrayValue) arrays[k])
1874: .contains(entryValue) != NullValue.NULL;
1875: }
1876:
1877: if (!valueFound)
1878: diffArray.put(entry.getKey(), entryValue);
1879: }
1880:
1881: return diffArray;
1882: }
1883:
1884: /*
1885: * Returns an array whose keys are the values of the keyArray passed in,
1886: * and whose values are all the value passed in.
1887: *
1888: * @param keyArray whose values are used to populate the keys of the new
1889: * array
1890: * @param value used as the value of the keys
1891: *
1892: * @return newly filled array
1893: */
1894: public ArrayValue array_fill_keys(Env env, ArrayValue keyArray,
1895: Value value) {
1896: ArrayValue array = new ArrayValueImpl();
1897:
1898: Iterator<Value> iter = keyArray.getValueIterator(env);
1899:
1900: while (iter.hasNext()) {
1901: array.put(iter.next(), value);
1902: }
1903:
1904: return array;
1905: }
1906:
1907: /**
1908: * Returns an array with everything that is in array and not in the other
1909: * arrays, keys also used
1910: *
1911: * @param array the primary array
1912: * @param arrays the vector of arrays to check the primary array's values
1913: * against
1914: * @return an array with all of the values that are in the primary array but
1915: * not in the other arrays
1916: */
1917: public Value array_diff_assoc(Env env, ArrayValue array,
1918: Value[] arrays) {
1919: if (array == null)
1920: return NullValue.NULL;
1921:
1922: if (arrays.length < 1) {
1923: env.warning("Wrong parameter count for array_diff()");
1924:
1925: return NullValue.NULL;
1926: }
1927:
1928: ArrayValue diffArray = new ArrayValueImpl();
1929:
1930: for (Map.Entry<Value, Value> entry : array.entrySet()) {
1931: boolean valueFound = false;
1932:
1933: Value entryValue = entry.getValue();
1934:
1935: Value entryKey = entry.getKey();
1936:
1937: for (int k = 0; k < arrays.length && !valueFound; k++) {
1938: if (!(arrays[k] instanceof ArrayValue)) {
1939: env.warning("Argument #" + (k + 2)
1940: + " is not an array");
1941:
1942: return NullValue.NULL;
1943: }
1944:
1945: valueFound = ((ArrayValue) arrays[k]).contains(
1946: entryValue).eq(entryKey);
1947: }
1948:
1949: if (!valueFound)
1950: diffArray.put(entryKey, entryValue);
1951: }
1952:
1953: return diffArray;
1954: }
1955:
1956: /**
1957: * Returns an array with everything that is in array and not in the other
1958: * arrays, keys used for comparison
1959: *
1960: * @param array the primary array
1961: * @param arrays the vector of arrays to check the primary array's values
1962: * against
1963: * @return an array with all of the values that are in the primary array but
1964: * not in the other arrays
1965: */
1966: public Value array_diff_key(Env env, ArrayValue array,
1967: Value[] arrays) {
1968: if (array == null)
1969: return NullValue.NULL;
1970:
1971: if (arrays.length < 1) {
1972: env.warning("Wrong parameter count for array_diff()");
1973:
1974: return NullValue.NULL;
1975: }
1976:
1977: ArrayValue diffArray = new ArrayValueImpl();
1978:
1979: for (Map.Entry<Value, Value> entry : array.entrySet()) {
1980: boolean keyFound = false;
1981:
1982: Value entryKey = entry.getKey();
1983:
1984: for (int k = 0; k < arrays.length && !keyFound; k++) {
1985: if (!(arrays[k] instanceof ArrayValue)) {
1986: env.warning("Argument #" + (k + 2)
1987: + " is not an array");
1988:
1989: return NullValue.NULL;
1990: }
1991:
1992: keyFound = ((ArrayValue) arrays[k])
1993: .containsKey(entryKey) != null;
1994: }
1995:
1996: if (!keyFound)
1997: diffArray.put(entryKey, entry.getValue());
1998: }
1999:
2000: return diffArray;
2001: }
2002:
2003: /**
2004: * Returns an array with everything that is in array and not in the other
2005: * arrays, keys used for comparison aswell
2006: *
2007: * @param array the primary array
2008: * @param arrays the vector of arrays to check the primary array's values
2009: * against. The last element is the callback function.
2010: * @return an array with all of the values that are in the primary array but
2011: * not in the other arrays
2012: */
2013: public Value array_diff_uassoc(Env env, ArrayValue array,
2014: Value[] arrays) {
2015: if (array == null)
2016: return NullValue.NULL;
2017:
2018: if (arrays.length < 2) {
2019: env.warning("Wrong parameter count for array_diff()");
2020:
2021: return NullValue.NULL;
2022: }
2023:
2024: AbstractFunction func = env
2025: .findFunction(arrays[arrays.length - 1].toString()
2026: .intern());
2027:
2028: if (func == null) {
2029: env.warning("Invalid comparison function");
2030:
2031: return NullValue.NULL;
2032: }
2033:
2034: ArrayValue diffArray = new ArrayValueImpl();
2035:
2036: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2037: boolean ValueFound = false;
2038:
2039: Value entryValue = entry.getValue();
2040:
2041: Value entryKey = entry.getKey();
2042:
2043: for (int k = 0; k < arrays.length - 1 && !ValueFound; k++) {
2044: if (!(arrays[k] instanceof ArrayValue)) {
2045: env.warning("Argument #" + (k + 2)
2046: + " is not an array");
2047:
2048: return NullValue.NULL;
2049: }
2050:
2051: Value searchKey = ((ArrayValue) arrays[k])
2052: .contains(entryValue);
2053:
2054: if (searchKey != NullValue.NULL)
2055: ValueFound = ((int) func.call(env, searchKey,
2056: entryKey).toLong()) == 0;
2057: }
2058:
2059: if (!ValueFound)
2060: diffArray.put(entryKey, entryValue);
2061: }
2062:
2063: return diffArray;
2064: }
2065:
2066: /**
2067: * Returns an array with everything that is in array and not in the other
2068: * arrays, keys used for comparison only
2069: *
2070: * @param array the primary array
2071: * @param arrays the vector of arrays to check the primary array's values
2072: * against. The last element is the callback function.
2073: * @return an array with all of the values that are in the primary array but
2074: * not in the other arrays
2075: */
2076: public Value array_diff_ukey(Env env, ArrayValue array,
2077: Value[] arrays) {
2078: if (array == null)
2079: return NullValue.NULL;
2080:
2081: if (arrays.length < 2) {
2082: env.warning("Wrong parameter count for array_diff()");
2083:
2084: return NullValue.NULL;
2085: }
2086:
2087: AbstractFunction func = env
2088: .findFunction(arrays[arrays.length - 1].toString()
2089: .intern());
2090:
2091: if (func == null) {
2092: env.warning("Invalid comparison function");
2093:
2094: return NullValue.NULL;
2095: }
2096:
2097: ArrayValue diffArray = new ArrayValueImpl();
2098:
2099: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2100: boolean keyFound = false;
2101:
2102: Value entryKey = entry.getKey();
2103:
2104: for (int k = 0; k < arrays.length - 1 && !keyFound; k++) {
2105: if (!(arrays[k] instanceof ArrayValue)) {
2106: env.warning("Argument #" + (k + 2)
2107: + " is not an array");
2108:
2109: return NullValue.NULL;
2110: }
2111:
2112: Iterator<Value> keyItr = ((ArrayValue) arrays[k])
2113: .keySet().iterator();
2114:
2115: keyFound = false;
2116:
2117: while (keyItr.hasNext() && !keyFound) {
2118: Value currentKey = keyItr.next();
2119:
2120: keyFound = ((int) func.call(env, entryKey,
2121: currentKey).toLong()) == 0;
2122: }
2123: }
2124:
2125: if (!keyFound)
2126: diffArray.put(entryKey, entry.getValue());
2127: }
2128:
2129: return diffArray;
2130: }
2131:
2132: /**
2133: * Returns an array with everything that is in array and also in the other
2134: * arrays
2135: *
2136: * @param array the primary array
2137: * @param arrays the vector of arrays to check the primary array's values
2138: * against. The last element is the callback function.
2139: * @return an array with all of the values that are in the primary array and
2140: * in the other arrays
2141: */
2142: public Value array_intersect(Env env, ArrayValue array,
2143: Value[] arrays) {
2144: if (array == null)
2145: return NullValue.NULL;
2146:
2147: if (arrays.length < 1) {
2148: env.warning("Wrong parameter count for array_diff()");
2149:
2150: return NullValue.NULL;
2151: }
2152:
2153: ArrayValue interArray = new ArrayValueImpl();
2154:
2155: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2156: boolean valueFound = false;
2157:
2158: Value entryValue = entry.getValue();
2159:
2160: for (int k = 0; k < arrays.length; k++) {
2161: if (!(arrays[k] instanceof ArrayValue)) {
2162: env.warning("Argument #" + (k + 2)
2163: + " is not an array");
2164:
2165: return NullValue.NULL;
2166: }
2167:
2168: if (k > 0 && !valueFound)
2169: break;
2170:
2171: valueFound = ((ArrayValue) arrays[k])
2172: .contains(entryValue) != NullValue.NULL;
2173: }
2174:
2175: if (valueFound)
2176: interArray.put(entry.getKey(), entryValue);
2177: }
2178:
2179: return interArray;
2180: }
2181:
2182: /**
2183: * Returns an array with everything that is in array and also in the other
2184: * arrays, keys are also used in the comparison
2185: *
2186: * @param array the primary array
2187: * @param arrays the vector of arrays to check the primary array's values
2188: * against. The last element is the callback function.
2189: * @return an array with all of the values that are in the primary array and
2190: * in the other arrays
2191: */
2192: public Value array_intersect_assoc(Env env, ArrayValue array,
2193: Value[] arrays) {
2194: if (array == null)
2195: return NullValue.NULL;
2196:
2197: if (arrays.length < 1) {
2198: env.warning("Wrong parameter count for array_diff()");
2199:
2200: return NullValue.NULL;
2201: }
2202:
2203: ArrayValue interArray = new ArrayValueImpl();
2204:
2205: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2206: boolean valueFound = false;
2207:
2208: Value entryKey = entry.getKey();
2209:
2210: Value entryValue = entry.getValue();
2211:
2212: for (int k = 0; k < arrays.length; k++) {
2213: if (!(arrays[k] instanceof ArrayValue)) {
2214: env.warning("Argument #" + (k + 2)
2215: + " is not an array");
2216:
2217: return NullValue.NULL;
2218: }
2219:
2220: if (k > 0 && !valueFound)
2221: break;
2222:
2223: Value searchValue = ((ArrayValue) arrays[k])
2224: .containsKey(entryKey);
2225:
2226: if (searchValue != null)
2227: valueFound = searchValue.eq(entryValue);
2228: else
2229: valueFound = false;
2230: }
2231:
2232: if (valueFound)
2233: interArray.put(entryKey, entryValue);
2234: }
2235:
2236: return interArray;
2237: }
2238:
2239: /**
2240: * Returns an array with everything that is in array and also in the other
2241: * arrays, keys are only used in the comparison
2242: *
2243: * @param array the primary array
2244: * @param arrays the vector of arrays to check the primary array's values
2245: * against. The last element is the callback function.
2246: * @return an array with all of the values that are in the primary array and
2247: * in the other arrays
2248: */
2249: public Value array_intersect_key(Env env, ArrayValue array,
2250: Value[] arrays) {
2251: if (array == null)
2252: return NullValue.NULL;
2253:
2254: if (arrays.length < 1) {
2255: env.warning("Wrong parameter count for array_diff()");
2256:
2257: return NullValue.NULL;
2258: }
2259:
2260: ArrayValue interArray = new ArrayValueImpl();
2261:
2262: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2263: boolean keyFound = false;
2264:
2265: Value entryKey = entry.getKey();
2266:
2267: for (int k = 0; k < arrays.length; k++) {
2268: if (!(arrays[k] instanceof ArrayValue)) {
2269: env.warning("Argument #" + (k + 2)
2270: + " is not an array");
2271:
2272: return NullValue.NULL;
2273: }
2274:
2275: if (k > 0 && !keyFound)
2276: break;
2277:
2278: keyFound = ((ArrayValue) arrays[k])
2279: .containsKey(entryKey) != null;
2280: }
2281:
2282: if (keyFound)
2283: interArray.put(entryKey, entry.getValue());
2284: }
2285:
2286: return interArray;
2287: }
2288:
2289: /**
2290: * Returns an array with everything that is in array and also in the other
2291: * arrays, keys are also used in the comparison. Uses a callback function for
2292: * evalutation the keys.
2293: *
2294: * @param array the primary array
2295: * @param arrays the vector of arrays to check the primary array's values
2296: * against. The last element is the callback function.
2297: * @return an array with all of the values that are in the primary array and
2298: * in the other arrays
2299: */
2300: public Value array_intersect_uassoc(Env env, ArrayValue array,
2301: Value[] arrays) {
2302: if (array == null)
2303: return NullValue.NULL;
2304:
2305: if (arrays.length < 2) {
2306: env.warning("Wrong parameter count for array_diff()");
2307:
2308: return NullValue.NULL;
2309: }
2310:
2311: AbstractFunction func = env
2312: .findFunction(arrays[arrays.length - 1].toString()
2313: .intern());
2314:
2315: if (func == null) {
2316: env.warning("Invalid comparison function");
2317:
2318: return NullValue.NULL;
2319: }
2320:
2321: ArrayValue interArray = new ArrayValueImpl();
2322:
2323: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2324: boolean valueFound = false;
2325:
2326: Value entryKey = entry.getKey();
2327:
2328: Value entryValue = entry.getValue();
2329:
2330: for (int k = 0; k < arrays.length - 1; k++) {
2331: if (!(arrays[k] instanceof ArrayValue)) {
2332: env.warning("Argument #" + (k + 2)
2333: + " is not an array");
2334:
2335: return NullValue.NULL;
2336: }
2337:
2338: if (k > 0 && !valueFound)
2339: break;
2340:
2341: Value searchValue = ((ArrayValue) arrays[k])
2342: .containsKey(entryKey);
2343:
2344: if (searchValue != null)
2345: valueFound = func
2346: .call(env, searchValue, entryValue)
2347: .toLong() == 0;
2348: else
2349: valueFound = false;
2350: }
2351:
2352: if (valueFound)
2353: interArray.put(entryKey, entryValue);
2354: }
2355:
2356: return interArray;
2357: }
2358:
2359: /**
2360: * Returns an array with everything that is in array and also in the other
2361: * arrays, keys are only used in the comparison. Uses a callback function for
2362: * evalutation the keys.
2363: *
2364: * @param array the primary array
2365: * @param arrays the vector of arrays to check the primary array's values
2366: * against. The last element is the callback function.
2367: * @return an array with all of the values that are in the primary array and
2368: * in the other arrays
2369: */
2370: public Value array_intersect_ukey(Env env, ArrayValue array,
2371: Value[] arrays) {
2372: if (array == null)
2373: return NullValue.NULL;
2374:
2375: if (arrays.length < 2) {
2376: env.warning("Wrong parameter count for array_diff()");
2377:
2378: return NullValue.NULL;
2379: }
2380:
2381: AbstractFunction func = env
2382: .findFunction(arrays[arrays.length - 1].toString()
2383: .intern());
2384:
2385: if (func == null) {
2386: env.warning("Invalid comparison function");
2387:
2388: return NullValue.NULL;
2389: }
2390:
2391: ArrayValue interArray = new ArrayValueImpl();
2392:
2393: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2394: boolean keyFound = false;
2395:
2396: Value entryKey = entry.getKey();
2397:
2398: for (int k = 0; k < arrays.length - 1; k++) {
2399: if (!(arrays[k] instanceof ArrayValue)) {
2400: env.warning("Argument #" + (k + 2)
2401: + " is not an array");
2402:
2403: return NullValue.NULL;
2404: }
2405:
2406: if (k > 0 && !keyFound)
2407: break;
2408:
2409: Iterator<Value> keyItr = ((ArrayValue) arrays[k])
2410: .keySet().iterator();
2411:
2412: keyFound = false;
2413:
2414: while (keyItr.hasNext() && !keyFound) {
2415: Value currentKey = keyItr.next();
2416:
2417: keyFound = ((int) func.call(env, entryKey,
2418: currentKey).toLong()) == 0;
2419: }
2420:
2421: }
2422:
2423: if (keyFound)
2424: interArray.put(entryKey, entry.getValue());
2425: }
2426:
2427: return interArray;
2428: }
2429:
2430: /**
2431: * Maps the given function with the array arguments.
2432: * XXX: callback modifying array?
2433: *
2434: * @param fun the function name
2435: * @param args the vector of array arguments
2436: * @return an array with all of the mapped values
2437: */
2438: public Value array_map(Env env, Callback fun, ArrayValue arg,
2439: Value[] args) {
2440: // quercus/1730
2441: Iterator<Map.Entry<Value, Value>> argIter = arg.entrySet()
2442: .iterator();
2443:
2444: Iterator[] iters = new Iterator[args.length];
2445: for (int i = 0; i < args.length; i++) {
2446: if (!(args[i] instanceof ArrayValue))
2447: throw env.createErrorException(L.l("expected array"));
2448:
2449: ArrayValue argArray = (ArrayValue) args[i];
2450:
2451: iters[i] = argArray.values().iterator();
2452: }
2453:
2454: ArrayValue resultArray = new ArrayValueImpl();
2455:
2456: Value[] param = new Value[args.length + 1];
2457: while (argIter.hasNext()) {
2458: Map.Entry<Value, Value> entry = argIter.next();
2459:
2460: param[0] = entry.getValue();
2461:
2462: for (int i = 0; i < iters.length; i++) {
2463: param[i + 1] = (Value) iters[i].next();
2464:
2465: if (param[i + 1] == null)
2466: param[i + 1] = NullValue.NULL;
2467: }
2468:
2469: resultArray.put(entry.getKey(), fun.call(env, param));
2470: }
2471:
2472: return resultArray;
2473: }
2474:
2475: /**
2476: * Maps the given function with the array arguments.
2477: *
2478: * @param args the vector of array arguments
2479: * @return an array with all of the mapped values
2480: */
2481: public Value array_merge(Value[] args) {
2482: // quercus/1731
2483:
2484: ArrayValue result = new ArrayValueImpl();
2485:
2486: for (Value arg : args) {
2487: if (arg.isNull())
2488: return NullValue.NULL;
2489:
2490: if (!(arg.toValue() instanceof ArrayValue))
2491: continue;
2492:
2493: ArrayValue array = (ArrayValue) arg.toValue();
2494:
2495: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2496: Value key = entry.getKey();
2497: Value value = entry.getValue();
2498:
2499: if (key.isNumberConvertible())
2500: result.put(value);
2501: else
2502: result.put(key, value);
2503: }
2504: }
2505:
2506: return result;
2507: }
2508:
2509: /**
2510: * Maps the given function with the array arguments.
2511: *
2512: * @param args the vector of array arguments
2513: * @return an array with all of the mapped values
2514: */
2515: public Value array_merge_recursive(Value[] args) {
2516: // quercus/173a
2517:
2518: ArrayValue result = new ArrayValueImpl();
2519:
2520: for (Value arg : args) {
2521: if (!(arg.toValue() instanceof ArrayValue))
2522: continue;
2523:
2524: arrayMergeRecursiveImpl(result, (ArrayValue) arg.toValue());
2525: }
2526:
2527: return result;
2528: }
2529:
2530: private static void arrayMergeRecursiveImpl(ArrayValue result,
2531: ArrayValue array) {
2532: for (Map.Entry<Value, Value> entry : array.entrySet()) {
2533: Value key = entry.getKey();
2534: Value value = entry.getValue().toValue();
2535:
2536: if (key.isNumberConvertible()) {
2537: result.put(value);
2538: } else {
2539: Value oldValue = result.get(key).toValue();
2540:
2541: if (oldValue != null && oldValue.isset()) {
2542: if (oldValue.isArray() && value.isArray()) {
2543: arrayMergeRecursiveImpl((ArrayValue) oldValue,
2544: (ArrayValue) value);
2545: } else if (oldValue.isArray()) {
2546: oldValue.put(value);
2547: } else if (value.isArray()) {
2548: // XXX: s/b insert?
2549: value.put(oldValue);
2550: } else {
2551: ArrayValue newArray = new ArrayValueImpl();
2552:
2553: newArray.put(oldValue);
2554: newArray.put(value);
2555:
2556: result.put(key, newArray);
2557: }
2558: } else {
2559: result.put(key, value);
2560: }
2561: }
2562: }
2563: }
2564:
2565: /**
2566: * Sort the arrays like rows in a database.
2567: * @param arrays arrays to sort
2568: *
2569: * @return true on success, and false on failure
2570: */
2571: public static boolean array_multisort(Env env, Value[] arrays) {
2572: boolean isNewKeys = true;
2573:
2574: if (arrays.length == 0 || !arrays[0].isArray()) {
2575: env.warning("the first argument must be an array");
2576:
2577: return false;
2578: }
2579:
2580: Value primary = arrays[0];
2581:
2582: Iterator<Value> keyIter = primary.getKeyIterator(env);
2583:
2584: while (keyIter.hasNext()) {
2585: if (!(keyIter.next() instanceof LongValue)) {
2586: isNewKeys = false;
2587: break;
2588: }
2589: }
2590:
2591: Value[] rows = primary.getKeyArray(env);
2592:
2593: int maxsize = 0;
2594: for (int i = 0; i < arrays.length; i++)
2595: if (arrays[i] instanceof ArrayValue)
2596: maxsize = Math.max(maxsize, arrays[i].getSize());
2597:
2598: // create the identity permutation [1..n]
2599: LongValue[] p = new LongValue[maxsize];
2600: for (int i = 0; i < rows.length; i++) {
2601: p[i] = LongValue.create(i);
2602: }
2603:
2604: java.util.Arrays.sort(p, new MultiSortComparator(env, rows,
2605: arrays));
2606:
2607: // apply the permuation
2608: for (int i = 0; i < arrays.length; i++) {
2609: if (arrays[i].isArray()) {
2610: permute(env, (ArrayValue) arrays[i], p, isNewKeys);
2611: }
2612: }
2613:
2614: return true;
2615: }
2616:
2617: /*
2618: * Apply a permutation to an array; on return, each element of
2619: * array[i] holds the value that was in array[permutation[i]]
2620: * before the call.
2621: */
2622: private static void permute(Env env, ArrayValue array,
2623: Value[] permutation, boolean isNewKeys) {
2624: Value[] keys = array.getKeyArray(env);
2625: Value[] values = array.getValueArray(env);
2626:
2627: array.clear();
2628:
2629: if (isNewKeys) {
2630: for (int i = 0; i < permutation.length; i++) {
2631: int p = permutation[i].toInt();
2632:
2633: Value value = values[p];
2634: array.put(LongValue.create(i), value.toValue().copy());
2635: }
2636: } else {
2637: for (int i = 0; i < permutation.length; i++) {
2638: int p = permutation[i].toInt();
2639:
2640: Value key = keys[p];
2641: Value value = values[p];
2642: array.put(key, value.toValue().copy());
2643: }
2644: }
2645: }
2646:
2647: // XXX: Performance Test asort
2648: /**
2649: * Sorts the array.
2650: */
2651: /*public Value asort(Env env,
2652: Value value,
2653: @Optional int mode)
2654: {
2655: if (! (value instanceof ArrayValue)) {
2656: env.warning(L.l("asort requires array at '{0}'", value));
2657: return BooleanValue.FALSE;
2658: }
2659:
2660: ArrayValue array = (ArrayValue) value;
2661:
2662: array.asort();
2663:
2664: return BooleanValue.TRUE;
2665: }*/
2666:
2667: // XXX: Performance Test ksort
2668: /**
2669: * Sorts the array.
2670: */
2671: /*public Value ksort(Env env,
2672: Value value,
2673: @Optional int mode)
2674: {
2675: if (! (value instanceof ArrayValue)) {
2676: env.warning(L.l("asort requires array at '{0}'", value));
2677: return BooleanValue.FALSE;
2678: }
2679:
2680: ArrayValue array = (ArrayValue) value;
2681:
2682: array.ksort();
2683:
2684: return BooleanValue.TRUE;
2685: }*/
2686:
2687: /**
2688: * Creates an array with all the values of the first array that are not
2689: * present in the other arrays, using a provided callback function to
2690: * determine equivalence.
2691: *
2692: * @param arrays first array is checked against the rest. Last element is the
2693: * callback function.
2694: * @return an array with all the values of the first array that are not in the
2695: * rest
2696: */
2697: public Value array_udiff(Env env, Value[] arrays) {
2698: if (arrays.length < 3) {
2699: env.warning("Wrong paremeter count for array_udiff()");
2700:
2701: return NullValue.NULL;
2702: }
2703:
2704: if (!(arrays[0] instanceof ArrayValue)) {
2705: env.warning("Argument #1 is not an array");
2706:
2707: return NullValue.NULL;
2708: }
2709:
2710: ArrayValue array = (ArrayValue) arrays[0];
2711:
2712: Value callbackValue = arrays[arrays.length - 1];
2713:
2714: Callback cmp;
2715:
2716: try {
2717: cmp = env.createCallback(callbackValue);
2718: } catch (Throwable t) {
2719: log.log(Level.WARNING, t.toString(), t);
2720:
2721: env.warning("Not a valid callback "
2722: + callbackValue.toString());
2723:
2724: return NullValue.NULL;
2725: }
2726:
2727: if (cmp == null) {
2728: env.warning("Not a valid callback "
2729: + callbackValue.toString());
2730:
2731: return NullValue.NULL;
2732: }
2733:
2734: ArrayValue diffArray = new ArrayValueImpl();
2735:
2736: boolean isFound = false;
2737:
2738: for (Value entryKey : array.keySet()) {
2739: Value entryValue = array.get(entryKey);
2740:
2741: for (int k = 1; k < arrays.length - 1 && !isFound; k++) {
2742: if (!(arrays[k] instanceof ArrayValue)) {
2743: env.warning("Argument #" + (k + 1)
2744: + " is not an array");
2745:
2746: return NullValue.NULL;
2747: }
2748:
2749: ArrayValue checkArray = (ArrayValue) arrays[k];
2750:
2751: for (Map.Entry<Value, Value> entry : checkArray
2752: .entrySet()) {
2753: try {
2754: isFound = cmp.call(env, entryValue,
2755: entry.getValue()).toLong() == 0;
2756: } catch (Throwable t) {
2757: log.log(Level.WARNING, t.toString(), t);
2758:
2759: env
2760: .warning("An error occurred while invoking the filter callback");
2761:
2762: return NullValue.NULL;
2763: }
2764:
2765: if (isFound)
2766: break;
2767: }
2768: }
2769:
2770: if (!isFound)
2771: diffArray.put(entryKey, entryValue);
2772:
2773: isFound = false;
2774: }
2775:
2776: return diffArray;
2777: }
2778:
2779: /**
2780: * Creates an array with all the values of the first array that are not
2781: * present in the other arrays, using a provided callback function to
2782: * determine equivalence. Also checks the key for equality using an internal
2783: * comparison function.
2784: *
2785: * @param arrays first array is checked against the rest. Last element is the
2786: * callback function.
2787: * @return an array with all the values of the first array that are not in the
2788: * rest
2789: */
2790: public Value array_udiff_assoc(Env env, Value[] arrays) {
2791: if (arrays.length < 3) {
2792: env
2793: .warning("Wrong paremeter count for array_udiff_assoc()");
2794:
2795: return NullValue.NULL;
2796: }
2797:
2798: if (!(arrays[0] instanceof ArrayValue)) {
2799: env.warning("Argument #1 is not an array");
2800:
2801: return NullValue.NULL;
2802: }
2803:
2804: ArrayValue array = (ArrayValue) arrays[0];
2805:
2806: Value callbackValue = arrays[arrays.length - 1];
2807:
2808: Callback cmp;
2809:
2810: try {
2811: cmp = env.createCallback(callbackValue);
2812: } catch (Throwable t) {
2813: log.log(Level.WARNING, t.toString(), t);
2814:
2815: env.warning("Not a valid callback "
2816: + callbackValue.toString());
2817:
2818: return NullValue.NULL;
2819: }
2820:
2821: if (cmp == null) {
2822: env.warning("Not a valid callback "
2823: + callbackValue.toString());
2824:
2825: return NullValue.NULL;
2826: }
2827:
2828: ArrayValue diffArray = new ArrayValueImpl();
2829:
2830: boolean isFound = false;
2831:
2832: for (Value entryKey : array.keySet()) {
2833: Value entryValue = array.get(entryKey);
2834:
2835: for (int k = 1; k < arrays.length - 1 && !isFound; k++) {
2836: if (!(arrays[k] instanceof ArrayValue)) {
2837: env.warning("Argument #" + (k + 1)
2838: + " is not an array");
2839:
2840: return NullValue.NULL;
2841: }
2842:
2843: ArrayValue checkArray = (ArrayValue) arrays[k];
2844:
2845: for (Map.Entry<Value, Value> entry : checkArray
2846: .entrySet()) {
2847: try {
2848: boolean keyFound = entryKey.eql(entry.getKey());
2849:
2850: boolean valueFound = false;
2851:
2852: if (keyFound)
2853: valueFound = cmp.call(env, entryValue,
2854: entry.getValue()).toLong() == 0;
2855:
2856: isFound = keyFound && valueFound;
2857: } catch (Throwable t) {
2858: log.log(Level.WARNING, t.toString(), t);
2859:
2860: env
2861: .warning("An error occurred while invoking the filter callback");
2862:
2863: return NullValue.NULL;
2864: }
2865:
2866: if (isFound)
2867: break;
2868: }
2869: }
2870:
2871: if (!isFound)
2872: diffArray.put(entryKey, entryValue);
2873:
2874: isFound = false;
2875: }
2876:
2877: return diffArray;
2878: }
2879:
2880: /**
2881: * Creates an array with all the values of the first array that are not
2882: * present in the other arrays, using a provided callback function to
2883: * determine equivalence. Also checks keys using a provided callback
2884: * function.
2885: *
2886: * @param arrays first array is checked against the rest. Last two elementare
2887: * the callback functions.
2888: * @return an array with all the values of the first array that are not in the
2889: * rest
2890: */
2891: public Value array_udiff_uassoc(Env env, Value[] arrays) {
2892: if (arrays.length < 4) {
2893: env
2894: .warning("Wrong paremeter count for array_udiff_uassoc()");
2895:
2896: return NullValue.NULL;
2897: }
2898:
2899: if (!(arrays[0] instanceof ArrayValue)) {
2900: env.warning("Argument #1 is not an array");
2901:
2902: return NullValue.NULL;
2903: }
2904:
2905: ArrayValue array = (ArrayValue) arrays[0];
2906:
2907: Value callbackValue = arrays[arrays.length - 2];
2908:
2909: Callback cmpValue;
2910:
2911: try {
2912: cmpValue = env.createCallback(callbackValue);
2913: } catch (Throwable t) {
2914: log.log(Level.WARNING, t.toString(), t);
2915:
2916: env.warning("Not a valid callback "
2917: + callbackValue.toString());
2918:
2919: return NullValue.NULL;
2920: }
2921:
2922: if (cmpValue == null) {
2923: env.warning("Not a valid callback "
2924: + callbackValue.toString());
2925:
2926: return NullValue.NULL;
2927: }
2928:
2929: Value callbackKey = arrays[arrays.length - 1];
2930:
2931: Callback cmpKey;
2932:
2933: try {
2934: cmpKey = env.createCallback(callbackKey);
2935: } catch (Throwable t) {
2936: log.log(Level.WARNING, t.toString(), t);
2937:
2938: env.warning("Not a valid callback "
2939: + callbackKey.toString());
2940:
2941: return NullValue.NULL;
2942: }
2943:
2944: if (cmpKey == null) {
2945: env.warning("Not a valid callback "
2946: + callbackKey.toString());
2947:
2948: return NullValue.NULL;
2949: }
2950:
2951: ArrayValue diffArray = new ArrayValueImpl();
2952:
2953: boolean isFound = false;
2954:
2955: for (Value entryKey : array.keySet()) {
2956: Value entryValue = array.get(entryKey);
2957:
2958: for (int k = 1; k < arrays.length - 2 && !isFound; k++) {
2959: if (!(arrays[k] instanceof ArrayValue)) {
2960: env.warning("Argument #" + (k + 1)
2961: + " is not an array");
2962:
2963: return NullValue.NULL;
2964: }
2965:
2966: ArrayValue checkArray = (ArrayValue) arrays[k];
2967:
2968: for (Map.Entry<Value, Value> entry : checkArray
2969: .entrySet()) {
2970: try {
2971: boolean valueFound = cmpValue.call(env,
2972: entryValue, entry.getValue()).toLong() == 0;
2973:
2974: boolean keyFound = false;
2975:
2976: if (valueFound)
2977: keyFound = cmpKey.call(env, entryKey,
2978: entry.getKey()).toLong() == 0;
2979:
2980: isFound = valueFound && keyFound;
2981: } catch (Throwable t) {
2982: log.log(Level.WARNING, t.toString(), t);
2983:
2984: env
2985: .warning("An error occurred while invoking the filter callback");
2986:
2987: return NullValue.NULL;
2988: }
2989:
2990: if (isFound)
2991: break;
2992: }
2993: }
2994:
2995: if (!isFound)
2996: diffArray.put(entryKey, entryValue);
2997:
2998: isFound = false;
2999: }
3000:
3001: return diffArray;
3002: }
3003:
3004: /**
3005: * Creates an array with all the values of the first array that are present in
3006: * the other arrays, using a provided callback function to determine
3007: * equivalence.
3008: * XXX: callback modifying arrays?
3009: *
3010: * @param arrays first array is checked against the rest. Last element is the
3011: * callback function.
3012: * @return an array with all the values of the first array that are in the
3013: * rest
3014: */
3015: public Value array_uintersect(Env env, Value[] arrays) {
3016: if (arrays.length < 3) {
3017: env.warning("Wrong paremeter count for array_uintersect()");
3018:
3019: return NullValue.NULL;
3020: }
3021:
3022: if (!(arrays[0] instanceof ArrayValue)) {
3023: env.warning("Argument #1 is not an array");
3024:
3025: return NullValue.NULL;
3026: }
3027:
3028: ArrayValue array = (ArrayValue) arrays[0];
3029:
3030: Value callbackValue = arrays[arrays.length - 1];
3031:
3032: Callback cmp;
3033:
3034: try {
3035: cmp = env.createCallback(callbackValue);
3036: } catch (Throwable t) {
3037: log.log(Level.WARNING, t.toString(), t);
3038:
3039: env.warning("Not a valid callback "
3040: + callbackValue.toString());
3041:
3042: return NullValue.NULL;
3043: }
3044:
3045: if (cmp == null) {
3046: env.warning("Not a valid callback "
3047: + callbackValue.toString());
3048:
3049: return NullValue.NULL;
3050: }
3051:
3052: ArrayValue interArray = new ArrayValueImpl();
3053:
3054: boolean isFound = true;
3055:
3056: for (Value entryKey : array.keySet()) {
3057: Value entryValue = array.get(entryKey);
3058:
3059: for (int k = 1; k < arrays.length - 1 && isFound; k++) {
3060: if (!(arrays[k] instanceof ArrayValue)) {
3061: env.warning("Argument #" + (k + 1)
3062: + " is not an array");
3063:
3064: return NullValue.NULL;
3065: }
3066:
3067: ArrayValue checkArray = (ArrayValue) arrays[k];
3068:
3069: for (Map.Entry<Value, Value> entry : checkArray
3070: .entrySet()) {
3071: try {
3072: isFound = cmp.call(env, entryValue,
3073: entry.getValue()).toLong() == 0;
3074: } catch (Throwable t) {
3075: log.log(Level.WARNING, t.toString(), t);
3076:
3077: env
3078: .warning("An error occurred while invoking the filter callback");
3079:
3080: return NullValue.NULL;
3081: }
3082:
3083: if (isFound)
3084: break;
3085: }
3086: }
3087:
3088: if (isFound)
3089: interArray.put(entryKey, entryValue);
3090: }
3091:
3092: return interArray;
3093: }
3094:
3095: /**
3096: * Creates an array with all the values of the first array that are present in
3097: * the other arrays, using a provided callback function to determine
3098: * equivalence. Also checks the keys for equivalence using an internal
3099: * comparison.
3100: * XXX: callback modifying arrays?
3101: *
3102: * @param arrays first array is checked against the rest. Last element is the
3103: * callback function.
3104: * @return an array with all the values of the first array that are in the
3105: * rest
3106: */
3107: public Value array_uintersect_assoc(Env env, Value[] arrays) {
3108: if (arrays.length < 3) {
3109: env
3110: .warning("Wrong paremeter count for array_uintersect_assoc()");
3111:
3112: return NullValue.NULL;
3113: }
3114:
3115: if (!(arrays[0] instanceof ArrayValue)) {
3116: env.warning("Argument #1 is not an array");
3117:
3118: return NullValue.NULL;
3119: }
3120:
3121: ArrayValue array = (ArrayValue) arrays[0];
3122:
3123: Value callbackValue = arrays[arrays.length - 1];
3124:
3125: Callback cmp;
3126:
3127: try {
3128: cmp = env.createCallback(callbackValue);
3129: } catch (Throwable t) {
3130: log.log(Level.WARNING, t.toString(), t);
3131:
3132: env.warning("Not a valid callback "
3133: + callbackValue.toString());
3134:
3135: return NullValue.NULL;
3136: }
3137:
3138: if (cmp == null) {
3139: env.warning("Not a valid callback "
3140: + callbackValue.toString());
3141:
3142: return NullValue.NULL;
3143: }
3144:
3145: ArrayValue interArray = new ArrayValueImpl();
3146:
3147: boolean isFound = true;
3148:
3149: for (Value entryKey : array.keySet()) {
3150: Value entryValue = array.get(entryKey);
3151:
3152: for (int k = 1; k < arrays.length - 1 && isFound; k++) {
3153: if (!(arrays[k] instanceof ArrayValue)) {
3154: env.warning("Argument #" + (k + 1)
3155: + " is not an array");
3156:
3157: return NullValue.NULL;
3158: }
3159:
3160: ArrayValue checkArray = (ArrayValue) arrays[k];
3161:
3162: for (Map.Entry<Value, Value> entry : checkArray
3163: .entrySet()) {
3164: try {
3165: boolean keyFound = entryKey.eql(entry.getKey());
3166:
3167: boolean valueFound = false;
3168:
3169: if (keyFound)
3170: valueFound = cmp.call(env, entryValue,
3171: entry.getValue()).toLong() == 0;
3172:
3173: isFound = keyFound && valueFound;
3174: } catch (Throwable t) {
3175: log.log(Level.WARNING, t.toString(), t);
3176:
3177: env
3178: .warning("An error occurred while invoking the filter callback");
3179:
3180: return NullValue.NULL;
3181: }
3182:
3183: if (isFound)
3184: break;
3185: }
3186: }
3187:
3188: if (isFound)
3189: interArray.put(entryKey, entryValue);
3190: }
3191:
3192: return interArray;
3193: }
3194:
3195: /**
3196: * Creates an array with all the values of the first array that are present in
3197: * the other arrays, using a provided callback function to determine
3198: * equivalence. Also checks the keys for equivalence using a pass callback
3199: * function
3200: * XXX: callback modifying arrays?
3201: *
3202: * @param arrays first array is checked against the rest. Last two elements
3203: * are the callback functions.
3204: * @return an array with all the values of the first array that are in the
3205: * rest
3206: */
3207: public Value array_uintersect_uassoc(Env env, Value[] arrays) {
3208: if (arrays.length < 4) {
3209: env
3210: .warning("Wrong paremeter count for array_uintersect_uassoc()");
3211:
3212: return NullValue.NULL;
3213: }
3214:
3215: if (!(arrays[0] instanceof ArrayValue)) {
3216: env.warning("Argument #1 is not an array");
3217:
3218: return NullValue.NULL;
3219: }
3220:
3221: ArrayValue array = (ArrayValue) arrays[0];
3222:
3223: Value callbackValue = arrays[arrays.length - 2];
3224:
3225: Callback cmpValue;
3226:
3227: try {
3228: cmpValue = env.createCallback(callbackValue);
3229: } catch (Throwable t) {
3230: log.log(Level.WARNING, t.toString(), t);
3231:
3232: env.warning("Not a valid callback "
3233: + callbackValue.toString());
3234:
3235: return NullValue.NULL;
3236: }
3237:
3238: if (cmpValue == null) {
3239: env.warning("Not a valid callback "
3240: + callbackValue.toString());
3241:
3242: return NullValue.NULL;
3243: }
3244:
3245: Value callbackKey = arrays[arrays.length - 1];
3246:
3247: Callback cmpKey;
3248:
3249: try {
3250: cmpKey = env.createCallback(callbackKey);
3251: } catch (Throwable t) {
3252: log.log(Level.WARNING, t.toString(), t);
3253:
3254: env.warning("Not a valid callback "
3255: + callbackKey.toString());
3256:
3257: return NullValue.NULL;
3258: }
3259:
3260: if (cmpKey == null) {
3261: env.warning("Not a valid callback "
3262: + callbackKey.toString());
3263:
3264: return NullValue.NULL;
3265: }
3266:
3267: ArrayValue interArray = new ArrayValueImpl();
3268:
3269: boolean isFound = true;
3270:
3271: for (Value entryKey : array.keySet()) {
3272: Value entryValue = array.get(entryKey);
3273:
3274: for (int k = 1; k < arrays.length - 2 && isFound; k++) {
3275: if (!(arrays[k] instanceof ArrayValue)) {
3276: env.warning("Argument #" + (k + 1)
3277: + " is not an array");
3278:
3279: return NullValue.NULL;
3280: }
3281:
3282: ArrayValue checkArray = (ArrayValue) arrays[k];
3283:
3284: for (Map.Entry<Value, Value> entry : checkArray
3285: .entrySet()) {
3286: try {
3287: boolean valueFound = cmpValue.call(env,
3288: entryValue, entry.getValue()).toLong() == 0;
3289:
3290: boolean keyFound = false;
3291:
3292: if (valueFound)
3293: keyFound = cmpKey.call(env, entryKey,
3294: entry.getKey()).toLong() == 0;
3295:
3296: isFound = valueFound && keyFound;
3297: } catch (Throwable t) {
3298: log.log(Level.WARNING, t.toString(), t);
3299:
3300: env
3301: .warning("An error occurred while invoking the filter callback");
3302:
3303: return NullValue.NULL;
3304: }
3305:
3306: if (isFound)
3307: break;
3308: }
3309: }
3310:
3311: if (isFound)
3312: interArray.put(entryKey, entryValue);
3313: }
3314:
3315: return interArray;
3316: }
3317:
3318: /**
3319: * Creates an array of corresponding values to variables in the symbol name.
3320: * The passed parameters are the names of the variables to be added to the
3321: * array.
3322: *
3323: * @param variables contains the names of variables to add to the array
3324: * @return an array with the values of variables that match those passed
3325: */
3326: @UsesSymbolTable
3327: public ArrayValue compact(Env env, Value[] variables) {
3328: ArrayValue compactArray = new ArrayValueImpl();
3329:
3330: for (Value variableName : variables) {
3331: if (variableName instanceof StringValue) {
3332: Value tableValue = env
3333: .getValue(variableName.toString());
3334:
3335: if (tableValue.isset())
3336: compactArray.put(variableName, tableValue);
3337: } else if (variableName instanceof ArrayValue) {
3338: ArrayValue array = (ArrayValue) variableName;
3339:
3340: ArrayValue innerArray = compact(env, array
3341: .valuesToArray());
3342:
3343: compactArray.putAll(innerArray);
3344: }
3345: }
3346:
3347: return compactArray;
3348: }
3349:
3350: /**
3351: * Returns the size of the array.
3352: */
3353: public static Value sizeof(Env env, @ReadOnly
3354: Value value, @Optional("false")
3355: boolean recursive) {
3356: return count(env, value, recursive);
3357: }
3358:
3359: private static class CompareString implements
3360: Comparator<Map.Entry<Value, Value>> {
3361: private AbstractGet _getter;
3362:
3363: private int _order;
3364:
3365: CompareString(AbstractGet getter, int order) {
3366: _getter = getter;
3367: _order = order;
3368: }
3369:
3370: public int compare(Map.Entry<Value, Value> aEntry,
3371: Map.Entry<Value, Value> bEntry) {
3372: String aElement = _getter.get(aEntry).toString();
3373: String bElement = _getter.get(bEntry).toString();
3374:
3375: return aElement.compareTo(bElement) * _order;
3376: }
3377: }
3378:
3379: private static class CompareNumeric implements
3380: Comparator<Map.Entry<Value, Value>> {
3381: private AbstractGet _getter;
3382:
3383: private int _order;
3384:
3385: CompareNumeric(AbstractGet getter, int order) {
3386: _getter = getter;
3387: _order = order;
3388: }
3389:
3390: public int compare(Map.Entry<Value, Value> aEntry,
3391: Map.Entry<Value, Value> bEntry) {
3392: try {
3393: // php/1756
3394: double aElement = _getter.get(aEntry).toDouble();
3395: double bElement = _getter.get(bEntry).toDouble();
3396:
3397: if (aElement == bElement)
3398: return 0;
3399: else if (aElement < bElement)
3400: return -1 * _order;
3401: else
3402: return _order;
3403: } catch (Throwable e) {
3404: throw new RuntimeException(e);
3405: }
3406: }
3407: }
3408:
3409: private static class CompareLocale implements
3410: Comparator<Map.Entry<Value, Value>> {
3411: private AbstractGet _getter;
3412:
3413: private int _order;
3414:
3415: private Collator _collator;
3416:
3417: CompareLocale(AbstractGet getter, int order, Collator collator) {
3418: _getter = getter;
3419: _order = order;
3420: _collator = collator;
3421: }
3422:
3423: public int compare(Map.Entry<Value, Value> aEntry,
3424: Map.Entry<Value, Value> bEntry) {
3425: String aElement = _getter.get(aEntry).toString();
3426: String bElement = _getter.get(bEntry).toString();
3427:
3428: return _collator.compare(aElement, bElement) * _order;
3429: }
3430: }
3431:
3432: private static class CompareNormal implements
3433: Comparator<Map.Entry<Value, Value>> {
3434: private AbstractGet _getter;
3435:
3436: private int _order;
3437:
3438: CompareNormal(AbstractGet getter, int order) {
3439: _getter = getter;
3440: _order = order;
3441: }
3442:
3443: public int compare(Map.Entry<Value, Value> aEntry,
3444: Map.Entry<Value, Value> bEntry) {
3445: if (_getter instanceof GetKey) {
3446: KeyComparator k = KeyComparator.CMP;
3447:
3448: return k.compare(aEntry, bEntry) * _order;
3449: }
3450:
3451: ValueComparator c = ValueComparator.CMP;
3452:
3453: return c.compare(aEntry, bEntry) * _order;
3454: }
3455: }
3456:
3457: private static class CompareNatural implements
3458: Comparator<Map.Entry<Value, Value>> {
3459: private AbstractGet _getter;
3460:
3461: private int _order;
3462:
3463: private boolean _isCaseSensitive;
3464:
3465: CompareNatural(AbstractGet getter, int order,
3466: boolean isCaseSensitive) {
3467: _getter = getter;
3468: _order = order;
3469: _isCaseSensitive = isCaseSensitive;
3470: }
3471:
3472: public int compare(Map.Entry<Value, Value> aEntry,
3473: Map.Entry<Value, Value> bEntry) {
3474: try {
3475: String aElement = _getter.get(aEntry).toString();
3476: String bElement = _getter.get(bEntry).toString();
3477:
3478: if (!_isCaseSensitive) {
3479: aElement = aElement.toLowerCase();
3480: bElement = bElement.toLowerCase();
3481: }
3482:
3483: StringParser aParser = new StringParser(aElement);
3484: StringParser bParser = new StringParser(bElement);
3485:
3486: while (aParser.hasNext() && bParser.hasNext()) {
3487: String aPart = aParser.next();
3488: String bPart = bParser.next();
3489:
3490: int comparison;
3491:
3492: try {
3493: Long aLong = Long.valueOf(aPart);
3494: Long bLong = Long.valueOf(bPart);
3495:
3496: comparison = aLong.compareTo(bLong);
3497: } catch (NumberFormatException e) {
3498: comparison = aPart.compareTo(bPart);
3499: }
3500:
3501: if (comparison < 0)
3502: return -1;
3503: else if (comparison > 0)
3504: return 1;
3505: }
3506:
3507: if (bParser.hasNext())
3508: return 1;
3509: else if (aParser.hasNext())
3510: return -1;
3511: else
3512: return 0;
3513:
3514: } catch (Throwable e) {
3515: throw new RuntimeException(e);
3516: }
3517: }
3518: }
3519:
3520: private static class CompareCallBack implements
3521: Comparator<Map.Entry<Value, Value>> {
3522: private AbstractGet _getter;
3523:
3524: private int _order;
3525:
3526: private Callback _func;
3527:
3528: private Env _env;
3529:
3530: CompareCallBack(AbstractGet getter, int order, Callback func,
3531: Env env) {
3532: _getter = getter;
3533: _order = order;
3534: _func = func;
3535: _env = env;
3536: }
3537:
3538: public int compare(Map.Entry<Value, Value> aEntry,
3539: Map.Entry<Value, Value> bEntry) {
3540: try {
3541: Value aElement = _getter.get(aEntry);
3542: Value bElement = _getter.get(bEntry);
3543:
3544: return (int) _func.call(_env, aElement, bElement)
3545: .toLong();
3546: } catch (Exception e) {
3547: throw new QuercusModuleException(e);
3548: }
3549: }
3550: }
3551:
3552: /*
3553: * A comparator used to sort a permutation based on a set of
3554: * column-arrays.
3555: */
3556: private static class MultiSortComparator implements
3557: Comparator<LongValue> {
3558:
3559: private final Env _env;
3560: private final Value[] _rows;
3561: private final Value[] _arrays;
3562:
3563: public MultiSortComparator(Env env, Value[] rows, Value[] arrays) {
3564: this ._env = env;
3565: this ._rows = rows;
3566: this ._arrays = arrays;
3567: }
3568:
3569: /*
3570: * Examine the "row" consisting of arrays[x][index1] and
3571: * arrays[x][index2] for all indices "x"; the permutation will be
3572: * sorted according to this comparison.
3573: */
3574: public int compare(LongValue index1, LongValue index2) {
3575: for (int i = 0; i < _arrays.length; i++) {
3576: // reset direction/mode for each array (per the php.net spec)
3577: int direction = SORT_ASC;
3578: int mode = SORT_REGULAR;
3579: ArrayValue av = (ArrayValue) _arrays[i];
3580:
3581: // process all flags appearing *after* an array but before the next one
3582: while ((i + 1) < _arrays.length
3583: && _arrays[i + 1] instanceof LongValue) {
3584: i++;
3585:
3586: int flag = _arrays[i].toInt();
3587:
3588: switch (flag) {
3589: case SORT_ASC:
3590: direction = SORT_ASC;
3591: break;
3592:
3593: case SORT_DESC:
3594: direction = SORT_DESC;
3595: break;
3596:
3597: case SORT_REGULAR:
3598: mode = SORT_REGULAR;
3599: break;
3600:
3601: case SORT_STRING:
3602: mode = SORT_STRING;
3603: break;
3604:
3605: case SORT_NUMERIC:
3606: mode = SORT_NUMERIC;
3607: break;
3608:
3609: default:
3610: _env
3611: .warning("Unknown sort flag: "
3612: + _arrays[i]);
3613: }
3614: }
3615:
3616: int cmp;
3617:
3618: Value lValue = av.get(_rows[index1.toInt()]);
3619: Value rValue = av.get(_rows[index2.toInt()]);
3620:
3621: if (mode == SORT_STRING) {
3622: // php/173g
3623: cmp = lValue.toString()
3624: .compareTo(rValue.toString());
3625: } else if (mode == SORT_NUMERIC) {
3626: // php/173f
3627: cmp = NumberValue.compareNum(lValue, rValue);
3628: } else
3629: cmp = lValue.cmp(rValue);
3630:
3631: if (cmp != 0)
3632: return direction == SORT_ASC ? cmp : -1 * cmp;
3633: }
3634:
3635: return 0;
3636: }
3637: }
3638:
3639: private static class StringParser {
3640: private int _current;
3641: private int _length;
3642:
3643: private String _string;
3644:
3645: private static final int SYMBOL = 1;
3646: private static final int LETTER = 2;
3647: private static final int DIGIT = 3;
3648:
3649: StringParser(String string) {
3650: _string = string;
3651: _length = string.length();
3652: _current = 0;
3653: }
3654:
3655: public boolean hasNext() {
3656: return _current < _length;
3657: }
3658:
3659: public String next() {
3660: int start;
3661: int type;
3662:
3663: try {
3664: char character = _string.charAt(_current);
3665:
3666: if (character == '0') {
3667: _current++;
3668: return "0";
3669: } else if (Character.isLetter(character))
3670: type = LETTER;
3671: else if (Character.isDigit(character))
3672: type = DIGIT;
3673: else
3674: type = SYMBOL;
3675:
3676: for (start = _current; _current < _length; _current++) {
3677: if (type == LETTER
3678: && Character.isLetter(_string
3679: .charAt(_current))) {
3680: } else if (type == DIGIT
3681: && Character.isDigit(_string
3682: .charAt(_current))) {
3683: } else if (type == SYMBOL
3684: && !Character.isLetterOrDigit(_string
3685: .charAt(_current))) {
3686: } else
3687: break;
3688: }
3689:
3690: return _string.substring(start, _current);
3691: } catch (Exception e) {
3692: log.log(Level.WARNING, e.toString(), e);
3693: return null;
3694: }
3695: }
3696: }
3697: }
|