Source Code Cross Referenced for ArrayModule.java in  » EJB-Server-resin-3.1.5 » quercus » com » caucho » quercus » lib » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » EJB Server resin 3.1.5 » quercus » com.caucho.quercus.lib 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.