Source Code Cross Referenced for StringModule.java in  » EJB-Server-resin-3.1.5 » quercus » com » caucho » quercus » lib » string » 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.string 
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.string;
0031:
0032:        import com.caucho.quercus.QuercusException;
0033:        import com.caucho.quercus.QuercusModuleException;
0034:        import com.caucho.quercus.annotation.NotNull;
0035:        import com.caucho.quercus.annotation.Optional;
0036:        import com.caucho.quercus.annotation.Reference;
0037:        import com.caucho.quercus.annotation.UsesSymbolTable;
0038:        import com.caucho.quercus.env.*;
0039:        import com.caucho.quercus.lib.file.BinaryOutput;
0040:        import com.caucho.quercus.lib.file.FileModule;
0041:        import com.caucho.quercus.module.AbstractQuercusModule;
0042:        import com.caucho.util.L10N;
0043:        import com.caucho.util.RandomUtil;
0044:        import com.caucho.vfs.ByteToChar;
0045:        import com.caucho.vfs.Path;
0046:
0047:        import java.io.IOException;
0048:        import java.io.InputStream;
0049:        import java.security.MessageDigest;
0050:        import java.text.DecimalFormat;
0051:        import java.text.DecimalFormatSymbols;
0052:        import java.text.NumberFormat;
0053:        import java.util.ArrayList;
0054:        import java.util.Arrays;
0055:        import java.util.Comparator;
0056:        import java.util.Currency;
0057:        import java.util.Iterator;
0058:        import java.util.Locale;
0059:        import java.util.Map;
0060:        import java.util.logging.Level;
0061:        import java.util.logging.Logger;
0062:        import java.util.zip.CRC32;
0063:
0064:        /**
0065:         * PHP functions implemented from the string module
0066:         */
0067:        public class StringModule extends AbstractQuercusModule {
0068:            private static final Logger log = Logger
0069:                    .getLogger(StringModule.class.getName());
0070:
0071:            private static final L10N L = new L10N(StringModule.class);
0072:
0073:            public static final int CRYPT_SALT_LENGTH = 2;
0074:            public static final int CRYPT_STD_DES = 0;
0075:            public static final int CRYPT_EXT_DES = 0;
0076:            public static final int CRYPT_MD5 = 0;
0077:            public static final int CRYPT_BLOWFISH = 0;
0078:
0079:            public static final int CHAR_MAX = 1;
0080:
0081:            public static final int LC_CTYPE = 1;
0082:            public static final int LC_NUMERIC = 2;
0083:            public static final int LC_TIME = 3;
0084:            public static final int LC_COLLATE = 4;
0085:            public static final int LC_MONETARY = 5;
0086:            public static final int LC_ALL = 6;
0087:            public static final int LC_MESSAGES = 7;
0088:
0089:            public static final int STR_PAD_LEFT = 1;
0090:            public static final int STR_PAD_RIGHT = 0;
0091:            public static final int STR_PAD_BOTH = 2;
0092:
0093:            private static final DecimalFormatSymbols DEFAULT_DECIMAL_FORMAT_SYMBOLS;
0094:
0095:            /**
0096:             * Escapes a string using C syntax.
0097:             *
0098:             * @see #stripcslashes
0099:             *
0100:             * @param source the source string to convert
0101:             * @param characters the set of characters to convert
0102:             * @return the escaped string
0103:             */
0104:            public static StringValue addcslashes(StringValue source,
0105:                    String characters) {
0106:                if (characters == null)
0107:                    characters = "";
0108:
0109:                boolean[] bitmap = parseCharsetBitmap(characters);
0110:
0111:                int length = source.length();
0112:
0113:                StringValue sb = source.createStringBuilder(length * 5 / 4);
0114:
0115:                for (int i = 0; i < length; i++) {
0116:                    char ch = source.charAt(i);
0117:
0118:                    if (ch >= 256 || !bitmap[ch]) {
0119:                        sb.append(ch);
0120:                        continue;
0121:                    }
0122:
0123:                    switch (ch) {
0124:                    case 0x07:
0125:                        sb.append("\\a");
0126:                        break;
0127:                    case '\b':
0128:                        sb.append("\\b");
0129:                        break;
0130:                    case '\t':
0131:                        sb.append("\\t");
0132:                        break;
0133:                    case '\n':
0134:                        sb.append("\\n");
0135:                        break;
0136:                    case 0xb:
0137:                        sb.append("\\v");
0138:                        break;
0139:                    case '\f':
0140:                        sb.append("\\f");
0141:                        break;
0142:                    case '\r':
0143:                        sb.append("\\r");
0144:                        break;
0145:                    default:
0146:                        if (ch < 0x20 || ch >= 0x7f) {
0147:                            // save as octal
0148:                            sb.append("\\");
0149:                            sb.append((char) ('0' + ((ch >> 6) & 7)));
0150:                            sb.append((char) ('0' + ((ch >> 3) & 7)));
0151:                            sb.append((char) ('0' + ((ch) & 7)));
0152:                            break;
0153:                        } else {
0154:                            sb.append("\\");
0155:                            sb.append(ch);
0156:                            break;
0157:                        }
0158:                    }
0159:                }
0160:
0161:                return sb;
0162:            }
0163:
0164:            /**
0165:             * Parses the cslashes bitmap returning an actual bitmap.
0166:             *
0167:             * @param charset the bitmap string
0168:             * @return  the actual bitmap
0169:             */
0170:            private static boolean[] parseCharsetBitmap(String charset) {
0171:                boolean[] bitmap = new boolean[256];
0172:
0173:                int length = charset.length();
0174:                for (int i = 0; i < length; i++) {
0175:                    char ch = charset.charAt(i);
0176:
0177:                    // XXX: the bitmap eventual might need to deal with unicode
0178:                    if (ch >= 256)
0179:                        continue;
0180:
0181:                    bitmap[ch] = true;
0182:
0183:                    if (length <= i + 3)
0184:                        continue;
0185:
0186:                    if (charset.charAt(i + 1) != '.'
0187:                            || charset.charAt(i + 2) != '.')
0188:                        continue;
0189:
0190:                    char last = charset.charAt(i + 3);
0191:
0192:                    if (last < ch) {
0193:                        // XXX: exception type
0194:                        throw new RuntimeException(L.l("Invalid range."));
0195:                    }
0196:
0197:                    i += 3;
0198:                    for (; ch <= last; ch++) {
0199:                        bitmap[ch] = true;
0200:                    }
0201:
0202:                    // XXX: handling of '@'?
0203:                }
0204:
0205:                return bitmap;
0206:            }
0207:
0208:            /**
0209:             * Escapes a string for db characters.
0210:             *
0211:             * @param source the source string to convert
0212:             * @return the escaped string
0213:             */
0214:            public static StringValue addslashes(StringValue source) {
0215:                StringValue sb = source
0216:                        .createStringBuilder(source.length() * 5 / 4);
0217:
0218:                int length = source.length();
0219:                for (int i = 0; i < length; i++) {
0220:                    char ch = source.charAt(i);
0221:
0222:                    switch (ch) {
0223:                    case 0x0:
0224:                        sb.append("\\0");
0225:                        break;
0226:                    case '\'':
0227:                        sb.append("\\'");
0228:                        break;
0229:                    case '\"':
0230:                        sb.append("\\\"");
0231:                        break;
0232:                    case '\\':
0233:                        sb.append("\\\\");
0234:                        break;
0235:                    default:
0236:                        sb.append(ch);
0237:                        break;
0238:                    }
0239:                }
0240:
0241:                return sb;
0242:            }
0243:
0244:            /**
0245:             * Converts a binary value to a hex value.
0246:             */
0247:            public static StringValue bin2hex(Env env, InputStream is) {
0248:                try {
0249:                    StringValue sb = env.createUnicodeBuilder();
0250:
0251:                    int ch;
0252:                    while ((ch = is.read()) >= 0) {
0253:                        int d = (ch >> 4) & 0xf;
0254:
0255:                        if (d < 10)
0256:                            sb.append((char) (d + '0'));
0257:                        else
0258:                            sb.append((char) (d + 'a' - 10));
0259:
0260:                        d = (ch) & 0xf;
0261:
0262:                        if (d < 10)
0263:                            sb.append((char) (d + '0'));
0264:                        else
0265:                            sb.append((char) (d + 'a' - 10));
0266:                    }
0267:
0268:                    return sb;
0269:                } catch (IOException e) {
0270:                    throw new QuercusModuleException(e);
0271:                }
0272:            }
0273:
0274:            /**
0275:             * Alias of rtrim.  Removes trailing whitespace.
0276:             *
0277:             * @param env the quercus environment
0278:             * @param str the string to be trimmed
0279:             * @param charset optional set of characters to trim
0280:             * @return the trimmed string
0281:             */
0282:            public static StringValue chop(Env env, StringValue str, @Optional
0283:            String charset) {
0284:                return rtrim(env, str, charset);
0285:            }
0286:
0287:            /**
0288:             * converts a number to its character equivalent
0289:             *
0290:             * @param value the integer value
0291:             *
0292:             * @return the string equivalent
0293:             */
0294:            public static String chr(long value) {
0295:                return String.valueOf((char) value);
0296:            }
0297:
0298:            /**
0299:             * Splits a string into chunks
0300:             *
0301:             * @param body the body string
0302:             * @param chunkLen the optional chunk length, defaults to 76
0303:             * @param end the optional end value, defaults to "\r\n"
0304:             */
0305:            public static String chunk_split(String body, @Optional("76")
0306:            int chunkLen, @Optional("\"\\r\\n\"")
0307:            String end) {
0308:                if (body == null)
0309:                    body = "";
0310:
0311:                if (end == null)
0312:                    end = "";
0313:
0314:                if (chunkLen < 1) // XXX: real exn
0315:                    throw new IllegalArgumentException(L.l("bad value {0}",
0316:                            chunkLen));
0317:
0318:                StringBuilder sb = new StringBuilder();
0319:
0320:                int i = 0;
0321:
0322:                for (; i + chunkLen <= body.length(); i += chunkLen) {
0323:                    sb.append(body.substring(i, i + chunkLen));
0324:                    sb.append(end);
0325:                }
0326:
0327:                if (i < body.length()) {
0328:                    sb.append(body.substring(i));
0329:                    sb.append(end);
0330:                }
0331:
0332:                return sb.toString();
0333:            }
0334:
0335:            /**
0336:             * Converts from one cyrillic set to another.
0337:             *
0338:             * This implementation does nothing, because quercus stores strings as
0339:             * 16 bit unicode.
0340:             */
0341:            public static String convert_cyr_string(Env env, String str,
0342:                    String from, String to) {
0343:                env.stub("convert_cyr_string");
0344:
0345:                return str;
0346:            }
0347:
0348:            public static Value convert_uudecode(Env env, String source) {
0349:                try {
0350:                    if (source == null || source.length() == 0)
0351:                        return BooleanValue.FALSE;
0352:
0353:                    ByteToChar byteToChar = env.getByteToChar();
0354:
0355:                    int length = source.length();
0356:
0357:                    int i = 0;
0358:                    while (i < length) {
0359:                        int ch1 = source.charAt(i++);
0360:
0361:                        if (ch1 == 0x60 || ch1 == 0x20)
0362:                            break;
0363:                        else if (ch1 < 0x20 || 0x5f < ch1)
0364:                            continue;
0365:
0366:                        int sublen = ch1 - 0x20;
0367:
0368:                        while (sublen > 0) {
0369:                            int code;
0370:
0371:                            code = ((source.charAt(i++) - 0x20) & 0x3f) << 18;
0372:                            code += ((source.charAt(i++) - 0x20) & 0x3f) << 12;
0373:                            code += ((source.charAt(i++) - 0x20) & 0x3f) << 6;
0374:                            code += ((source.charAt(i++) - 0x20) & 0x3f);
0375:
0376:                            byteToChar.addByte(code >> 16);
0377:
0378:                            if (sublen > 1)
0379:                                byteToChar.addByte(code >> 8);
0380:
0381:                            if (sublen > 2)
0382:                                byteToChar.addByte(code);
0383:
0384:                            sublen -= 3;
0385:                        }
0386:                    }
0387:
0388:                    return env.createString(byteToChar.getConvertedString());
0389:                } catch (IOException e) {
0390:                    throw new QuercusModuleException(e);
0391:                }
0392:            }
0393:
0394:            /**
0395:             * uuencode a string.
0396:             */
0397:            public static Value convert_uuencode(StringValue source) {
0398:                if (source == null || source.length() == 0)
0399:                    return BooleanValue.FALSE;
0400:
0401:                StringValue result = source.createStringBuilder();
0402:
0403:                int i = 0;
0404:                int length = source.length();
0405:                while (i < length) {
0406:                    int sublen = length - i;
0407:
0408:                    if (45 < sublen)
0409:                        sublen = 45;
0410:
0411:                    result.append((char) (sublen + 0x20));
0412:
0413:                    int end = i + sublen;
0414:
0415:                    while (i < end) {
0416:                        int code = source.charAt(i++) << 16;
0417:
0418:                        if (i < length)
0419:                            code += source.charAt(i++) << 8;
0420:
0421:                        if (i < length)
0422:                            code += source.charAt(i++);
0423:
0424:                        result.append(toUUChar(((code >> 18) & 0x3f)));
0425:                        result.append(toUUChar(((code >> 12) & 0x3f)));
0426:                        result.append(toUUChar(((code >> 6) & 0x3f)));
0427:                        result.append(toUUChar(((code) & 0x3f)));
0428:                    }
0429:
0430:                    result.append('\n');
0431:                }
0432:
0433:                result.append((char) 0x60);
0434:                result.append('\n');
0435:
0436:                return result;
0437:            }
0438:
0439:            /**
0440:             * Returns an array of information about the characters.
0441:             */
0442:            public static Value count_chars(StringValue data, @Optional("0")
0443:            int mode) {
0444:                if (data == null)
0445:                    data = StringValue.EMPTY;
0446:
0447:                int[] count = new int[256];
0448:
0449:                int length = data.length();
0450:
0451:                for (int i = 0; i < length; i++) {
0452:                    count[data.charAt(i) & 0xff] += 1;
0453:                }
0454:
0455:                switch (mode) {
0456:                case 0: {
0457:                    ArrayValue result = new ArrayValueImpl();
0458:
0459:                    for (int i = 0; i < count.length; i++) {
0460:                        result.put(LongValue.create(i), LongValue
0461:                                .create(count[i]));
0462:                    }
0463:
0464:                    return result;
0465:                }
0466:
0467:                case 1: {
0468:                    ArrayValue result = new ArrayValueImpl();
0469:
0470:                    for (int i = 0; i < count.length; i++) {
0471:                        if (count[i] > 0)
0472:                            result.put(LongValue.create(i), new LongValue(
0473:                                    count[i]));
0474:                    }
0475:
0476:                    return result;
0477:                }
0478:
0479:                case 2: {
0480:                    ArrayValue result = new ArrayValueImpl();
0481:
0482:                    for (int i = 0; i < count.length; i++) {
0483:                        if (count[i] == 0)
0484:                            result.put(new LongValue(i),
0485:                                    new LongValue(count[i]));
0486:                    }
0487:
0488:                    return result;
0489:                }
0490:
0491:                case 3: {
0492:                    StringValue sb = data.createStringBuilder();
0493:
0494:                    for (int i = 0; i < count.length; i++) {
0495:                        if (count[i] > 0)
0496:                            sb.append((char) i);
0497:                    }
0498:
0499:                    return sb;
0500:                }
0501:
0502:                case 4: {
0503:                    StringValue sb = data.createStringBuilder();
0504:
0505:                    for (int i = 0; i < count.length; i++) {
0506:                        if (count[i] == 0)
0507:                            sb.append((char) i);
0508:                    }
0509:
0510:                    return sb;
0511:                }
0512:
0513:                default:
0514:                    return BooleanValue.FALSE;
0515:                }
0516:            }
0517:
0518:            /**
0519:             * Calculates the crc32 value for a string
0520:             *
0521:             * @param str the string value
0522:             *
0523:             * @return the crc32 hash
0524:             */
0525:            public static long crc32(InputStream is) {
0526:                try {
0527:                    CRC32 crc = new CRC32();
0528:
0529:                    int ch;
0530:                    while ((ch = is.read()) >= 0) {
0531:                        crc.update((byte) ch);
0532:                    }
0533:
0534:                    return crc.getValue() & 0xffffffff;
0535:                } catch (IOException e) {
0536:                    throw new QuercusModuleException(e);
0537:                }
0538:            }
0539:
0540:            public static String crypt(String string, @Optional
0541:            String salt) {
0542:                if (string == null)
0543:                    string = "";
0544:
0545:                if (salt == null || salt.equals("")) {
0546:                    salt = ("" + Crypt.resultToChar(RandomUtil.nextInt(0x40)) + Crypt
0547:                            .resultToChar(RandomUtil.nextInt(0x40)));
0548:                }
0549:
0550:                return Crypt.crypt(string, salt);
0551:            }
0552:
0553:            /**
0554:             * Explodes a string into an array
0555:             *
0556:             * @param separator the separator string
0557:             * @param string the string to be exploded
0558:             * @param limit the max number of elements
0559:             * @return an array of exploded values
0560:             */
0561:            public static Value explode(StringValue separator,
0562:                    StringValue string, @Optional("0x7fffffff")
0563:                    long limit) {
0564:                if (separator.length() == 0)
0565:                    return BooleanValue.FALSE;
0566:
0567:                ArrayValue array = new ArrayValueImpl();
0568:
0569:                int head = 0;
0570:                int tail;
0571:
0572:                int i = 0;
0573:                while ((tail = string.indexOf(separator, head)) >= 0) {
0574:                    if (limit <= i + 1)
0575:                        break;
0576:
0577:                    LongValue key = LongValue.create(i++);
0578:
0579:                    StringValue chunk = string.substring(head, tail);
0580:
0581:                    array.put(key, chunk);
0582:
0583:                    head = tail + separator.length();
0584:                }
0585:
0586:                LongValue key = LongValue.create(i);
0587:
0588:                StringValue chunk = string.substring(head);
0589:
0590:                array.put(key, chunk);
0591:
0592:                return array;
0593:            }
0594:
0595:            /**
0596:             * Use printf style formatting to write a string to a file.
0597:             * @param fd the file to write to
0598:             * @param format the format string
0599:             * @param args the valujes to apply to the format string
0600:             */
0601:            public static Value fprintf(Env env, @NotNull
0602:            BinaryOutput os, StringValue format, Value[] args) {
0603:                Value value = sprintf(format, args);
0604:
0605:                return FileModule.fwrite(env, os, value.toInputStream(),
0606:                        Integer.MAX_VALUE);
0607:            }
0608:
0609:            /**
0610:             * implodes an array into a string
0611:             *
0612:             * @param glueV the separator string
0613:             * @param piecesV the array to be imploded
0614:             *
0615:             * @return a string of imploded values
0616:             */
0617:            public static Value implode(Env env, Value glueV, @Optional
0618:            Value piecesV) {
0619:                StringValue glue;
0620:                ArrayValue pieces;
0621:
0622:                if (piecesV.isArray()) {
0623:                    pieces = piecesV.toArrayValue(env);
0624:                    glue = glueV.toStringValue();
0625:                } else if (glueV.isArray()) {
0626:                    pieces = glueV.toArrayValue(env);
0627:                    glue = piecesV.toStringValue();
0628:                } else {
0629:                    env
0630:                            .warning(L
0631:                                    .l(
0632:                                            "neither argument to implode is an array: {0}, {1}",
0633:                                            glueV.getClass().getName(), piecesV
0634:                                                    .getClass().getName()));
0635:
0636:                    return NullValue.NULL;
0637:                }
0638:
0639:                StringValue sb = glue.createStringBuilder();
0640:                boolean isFirst = true;
0641:
0642:                for (ArrayValue.Entry entry = pieces.getHead(); entry != null; entry = entry
0643:                        .getNext()) {
0644:                    if (!isFirst)
0645:                        sb = sb.append(glue);
0646:
0647:                    isFirst = false;
0648:
0649:                    sb = sb.append(entry.getValue());
0650:                }
0651:
0652:                return sb;
0653:            }
0654:
0655:            /**
0656:             * implodes an array into a string
0657:             *
0658:             * @param glueV the separator string
0659:             * @param piecesV the array to be imploded
0660:             *
0661:             * @return a string of imploded values
0662:             */
0663:            public static Value join(Env env, Value glueV, Value piecesV) {
0664:                return implode(env, glueV, piecesV);
0665:            }
0666:
0667:            /**
0668:             * returns the md5 hash
0669:             *
0670:             * @param source the string
0671:             * @param rawOutput if true, return the raw binary
0672:             *
0673:             * @return a string of imploded values
0674:             */
0675:            public static StringValue md5(Env env, InputStream is, @Optional
0676:            boolean rawOutput) {
0677:                try {
0678:                    MessageDigest md = MessageDigest.getInstance("MD5");
0679:
0680:                    // XXX: iso-8859-1
0681:
0682:                    int ch;
0683:                    while ((ch = is.read()) >= 0) {
0684:                        md.update((byte) ch);
0685:                    }
0686:
0687:                    byte[] digest = md.digest();
0688:
0689:                    StringValue sb = env.createUnicodeBuilder();
0690:                    for (int i = 0; i < digest.length; i++) {
0691:                        int d1 = (digest[i] >> 4) & 0xf;
0692:                        int d2 = (digest[i] & 0xf);
0693:
0694:                        sb.append(toHexChar(d1));
0695:                        sb.append(toHexChar(d2));
0696:                    }
0697:
0698:                    return sb;
0699:                } catch (Exception e) {
0700:                    throw new QuercusModuleException(e);
0701:                }
0702:            }
0703:
0704:            /**
0705:             * returns the md5 hash
0706:             *
0707:             * @param source the string
0708:             * @param rawOutput if true, return the raw binary
0709:             *
0710:             * @return a string of imploded values
0711:             */
0712:            public static Value md5_file(Env env, Path source, @Optional
0713:            boolean rawOutput) {
0714:                try {
0715:                    MessageDigest md = MessageDigest.getInstance("MD5");
0716:                    InputStream is = null;
0717:
0718:                    try {
0719:                        is = source.openRead();
0720:                        int d;
0721:
0722:                        while ((d = is.read()) >= 0) {
0723:                            md.update((byte) d);
0724:                        }
0725:
0726:                        return digestToString(env, md.digest());
0727:                    } catch (IOException e) {
0728:                        log.log(Level.FINE, e.toString(), e);
0729:
0730:                        return BooleanValue.FALSE;
0731:                    } finally {
0732:                        try {
0733:                            if (is != null)
0734:                                is.close();
0735:                        } catch (IOException e) {
0736:                        }
0737:                    }
0738:                } catch (Exception e) {
0739:                    throw new QuercusModuleException(e);
0740:                }
0741:            }
0742:
0743:            private static StringValue digestToString(Env env, byte[] digest) {
0744:                StringValue sb = env.createUnicodeBuilder();
0745:                for (int i = 0; i < digest.length; i++) {
0746:                    int d1 = (digest[i] >> 4) & 0xf;
0747:                    int d2 = (digest[i] & 0xf);
0748:
0749:                    sb.append(toHexChar(d1));
0750:                    sb.append(toHexChar(d2));
0751:                }
0752:
0753:                return sb;
0754:            }
0755:
0756:            /**
0757:             * Returns a formatted money value.
0758:             *
0759:             * @param format the format
0760:             * @param value the value
0761:             *
0762:             * @return a string of formatted values
0763:             */
0764:            public static String money_format(Env env, String format,
0765:                    double value) {
0766:                Locale monetaryLocale = env.getLocaleInfo().getMonetary();
0767:
0768:                return NumberFormat.getCurrencyInstance(monetaryLocale).format(
0769:                        value);
0770:            }
0771:
0772:            /**
0773:             * Returns the metaphone of a string.
0774:             * This implentation produces identical results to the php version, which does contain some bugs.
0775:             */
0776:            public static String metaphone(String string) {
0777:                if (string == null)
0778:                    string = "";
0779:
0780:                int length = string.length();
0781:                int index = 0;
0782:                char ch = 0;
0783:
0784:                // ignore everything up until first letter
0785:                for (; index < length; index++) {
0786:                    ch = toUpperCase(string.charAt(index));
0787:
0788:                    if ('A' <= ch && ch <= 'Z')
0789:                        break;
0790:                }
0791:
0792:                if (index == length)
0793:                    return "";
0794:
0795:                int lastIndex = length - 1;
0796:
0797:                StringBuilder result = new StringBuilder(length);
0798:
0799:                // special case first letter
0800:
0801:                char nextCh = index < lastIndex ? toUpperCase(string
0802:                        .charAt(index + 1)) : 0;
0803:
0804:                switch (ch) {
0805:                case 'A':
0806:                    if (nextCh == 'E') {
0807:                        result.append('E');
0808:                        index += 2;
0809:                    } else {
0810:                        result.append('A');
0811:                        index += 1;
0812:                    }
0813:
0814:                    break;
0815:
0816:                case 'E':
0817:                case 'I':
0818:                case 'O':
0819:                case 'U':
0820:                    result.append(ch);
0821:                    index += 1;
0822:                    break;
0823:
0824:                case 'G':
0825:                case 'K':
0826:                case 'P':
0827:                    if (nextCh == 'N') {
0828:                        result.append('N');
0829:                        index += 2;
0830:                    }
0831:
0832:                    break;
0833:
0834:                case 'W':
0835:                    if (nextCh == 'H' || nextCh == 'R') {
0836:                        result.append(nextCh);
0837:                        index += 2;
0838:                    } else {
0839:                        switch (nextCh) {
0840:                        case 'A':
0841:                        case 'E':
0842:                        case 'I':
0843:                        case 'O':
0844:                        case 'U':
0845:                            result.append('W');
0846:                            index += 2;
0847:                            break;
0848:                        default:
0849:                            break;
0850:                        }
0851:                    }
0852:
0853:                    break;
0854:
0855:                case 'X':
0856:                    result.append('S');
0857:                    index += 1;
0858:                    break;
0859:
0860:                default:
0861:                    break;
0862:                }
0863:
0864:                // the rest of the letters
0865:
0866:                char prevCh;
0867:
0868:                for (; index < length; index++) {
0869:
0870:                    if (index > 0)
0871:                        prevCh = toUpperCase(string.charAt(index - 1));
0872:                    else
0873:                        prevCh = 0;
0874:
0875:                    ch = toUpperCase(string.charAt(index));
0876:
0877:                    if (ch < 'A' || ch > 'Z')
0878:                        continue;
0879:
0880:                    if (ch == prevCh && ch != 'C')
0881:                        continue;
0882:
0883:                    if (index + 1 < length)
0884:                        nextCh = toUpperCase(string.charAt(index + 1));
0885:                    else
0886:                        nextCh = 0;
0887:
0888:                    char nextnextCh;
0889:
0890:                    if (index + 2 < length)
0891:                        nextnextCh = toUpperCase(string.charAt(index + 2));
0892:                    else
0893:                        nextnextCh = 0;
0894:
0895:                    switch (ch) {
0896:                    case 'B':
0897:                        if (prevCh != 'M')
0898:                            result.append('B');
0899:                        break;
0900:
0901:                    case 'C':
0902:                        switch (nextCh) {
0903:                        case 'E':
0904:                        case 'I':
0905:                        case 'Y':
0906:                            // makesoft
0907:                            if (nextCh == 'I' && nextnextCh == 'A') {
0908:                                result.append('X');
0909:                            } else if (prevCh == 'S') {
0910:                            } else {
0911:                                result.append('S');
0912:                            }
0913:                            break;
0914:                        default:
0915:                            if (nextCh == 'H') {
0916:                                result.append('X');
0917:                                index++;
0918:                            } else {
0919:                                result.append('K');
0920:                            }
0921:                            break;
0922:                        }
0923:
0924:                        break;
0925:
0926:                    case 'D':
0927:                        if (nextCh == 'G') {
0928:                            switch (nextnextCh) {
0929:                            case 'E':
0930:                            case 'I':
0931:                            case 'Y':
0932:                                // makesoft
0933:                                result.append('J');
0934:                                index++;
0935:                                break;
0936:                            default:
0937:                                result.append('T');
0938:                                break;
0939:                            }
0940:                        } else
0941:                            result.append('T');
0942:
0943:                        break;
0944:
0945:                    case 'G':
0946:                        if (nextCh == 'H') {
0947:                            boolean isSilent = false;
0948:
0949:                            if (index - 3 >= 0) {
0950:                                char prev3Ch = toUpperCase(string
0951:                                        .charAt(index - 3));
0952:                                switch (prev3Ch) {
0953:                                // noghtof
0954:                                case 'B':
0955:                                case 'D':
0956:                                case 'H':
0957:                                    isSilent = true;
0958:                                    break;
0959:                                default:
0960:                                    break;
0961:                                }
0962:                            }
0963:
0964:                            if (!isSilent) {
0965:                                if (index - 4 >= 0) {
0966:                                    char prev4Ch = toUpperCase(string
0967:                                            .charAt(index - 4));
0968:
0969:                                    isSilent = (prev4Ch == 'H');
0970:                                }
0971:                            }
0972:
0973:                            if (!isSilent) {
0974:                                result.append('F');
0975:                                index++;
0976:                            }
0977:                        } else if (nextCh == 'N') {
0978:                            char nextnextnextCh;
0979:
0980:                            if (index + 3 < length)
0981:                                nextnextnextCh = toUpperCase(string
0982:                                        .charAt(index + 3));
0983:                            else
0984:                                nextnextnextCh = 0;
0985:
0986:                            if (nextnextCh < 'A' || nextnextCh > 'Z') {
0987:                            } else if (nextnextCh == 'E'
0988:                                    && nextnextnextCh == 'D') {
0989:                            } else
0990:                                result.append('K');
0991:                        } else if (prevCh == 'G') {
0992:                            result.append('K');
0993:                        } else {
0994:                            switch (nextCh) {
0995:                            case 'E':
0996:                            case 'I':
0997:                            case 'Y':
0998:                                // makesoft
0999:                                result.append('J');
1000:                                break;
1001:                            default:
1002:                                result.append('K');
1003:                                break;
1004:                            }
1005:                        }
1006:
1007:                        break;
1008:
1009:                    case 'H':
1010:                    case 'W':
1011:                    case 'Y':
1012:                        switch (nextCh) {
1013:                        case 'A':
1014:                        case 'E':
1015:                        case 'I':
1016:                        case 'O':
1017:                        case 'U':
1018:                            // followed by a vowel
1019:
1020:                            if (ch == 'H') {
1021:                                switch (prevCh) {
1022:                                case 'C':
1023:                                case 'G':
1024:                                case 'P':
1025:                                case 'S':
1026:                                case 'T':
1027:                                    // affecth
1028:                                    break;
1029:                                default:
1030:                                    result.append('H');
1031:                                    break;
1032:                                }
1033:                            } else
1034:                                result.append(ch);
1035:
1036:                            break;
1037:                        default:
1038:                            // not followed by a vowel
1039:                            break;
1040:                        }
1041:
1042:                        break;
1043:
1044:                    case 'K':
1045:                        if (prevCh != 'C')
1046:                            result.append('K');
1047:
1048:                        break;
1049:
1050:                    case 'P':
1051:                        if (nextCh == 'H')
1052:                            result.append('F');
1053:                        else
1054:                            result.append('P');
1055:
1056:                        break;
1057:
1058:                    case 'Q':
1059:                        result.append('K');
1060:                        break;
1061:
1062:                    case 'S':
1063:                        if (nextCh == 'I'
1064:                                && (nextnextCh == 'O' || nextnextCh == 'A')) {
1065:                            result.append('X');
1066:                        } else if (nextCh == 'H') {
1067:                            result.append('X');
1068:                            index++;
1069:                        } else
1070:                            result.append('S');
1071:
1072:                        break;
1073:
1074:                    case 'T':
1075:                        if (nextCh == 'I'
1076:                                && (nextnextCh == 'O' || nextnextCh == 'A')) {
1077:                            result.append('X');
1078:                        } else if (nextCh == 'H') {
1079:                            result.append('0');
1080:                            index++;
1081:                        } else
1082:                            result.append('T');
1083:
1084:                        break;
1085:
1086:                    case 'V':
1087:                        result.append('F');
1088:
1089:                        break;
1090:
1091:                    case 'X':
1092:                        result.append('K');
1093:                        result.append('S');
1094:                        break;
1095:
1096:                    case 'Z':
1097:                        result.append('S');
1098:                        break;
1099:
1100:                    case 'F':
1101:                    case 'J':
1102:                    case 'L':
1103:                    case 'M':
1104:                    case 'N':
1105:                    case 'R':
1106:                        result.append(ch);
1107:                        break;
1108:
1109:                    default:
1110:                        break;
1111:                    }
1112:                }
1113:
1114:                return result.toString();
1115:            }
1116:
1117:            /**
1118:             * Returns a formatted number.
1119:             *
1120:             * @param value the value
1121:             * @param decimals the number of decimals
1122:             * @param pointValue the decimal point string
1123:             * @param groupValue the thousands separator
1124:             *
1125:             * @return a string of the formatted number
1126:             */
1127:            public static String number_format(Env env, double value, @Optional
1128:            int decimals, @Optional
1129:            Value pointValue, @Optional
1130:            Value groupValue) {
1131:                boolean isGroupDefault = (groupValue instanceof  DefaultValue);
1132:                boolean isPointDefault = (pointValue instanceof  DefaultValue);
1133:
1134:                if (!isPointDefault && isGroupDefault) {
1135:                    env.warning(L.l("wrong parameter count"));
1136:                    return null;
1137:                }
1138:
1139:                String pattern;
1140:
1141:                char point = '.';
1142:
1143:                if (!pointValue.isNull()) {
1144:                    String pointString = pointValue.toString();
1145:
1146:                    point = (pointString.length() == 0) ? 0 : pointString
1147:                            .charAt(0);
1148:                }
1149:
1150:                char group = ',';
1151:
1152:                if (!groupValue.isNull()) {
1153:                    String groupString = groupValue.toString();
1154:
1155:                    group = (groupString.length() == 0) ? 0 : groupString
1156:                            .charAt(0);
1157:                }
1158:
1159:                if (decimals > 0) {
1160:                    StringBuilder patternBuilder = new StringBuilder(
1161:                            6 + decimals);
1162:
1163:                    patternBuilder.append(group == 0 ? "###0." : "#,##0.");
1164:
1165:                    for (int i = 0; i < decimals; i++)
1166:                        patternBuilder.append('0');
1167:
1168:                    pattern = patternBuilder.toString();
1169:                } else {
1170:                    pattern = group == 0 ? "###0" : "#,##0";
1171:                }
1172:
1173:                DecimalFormatSymbols decimalFormatSymbols;
1174:
1175:                if (point == '.' && group == ',')
1176:                    decimalFormatSymbols = DEFAULT_DECIMAL_FORMAT_SYMBOLS;
1177:                else {
1178:                    decimalFormatSymbols = new DecimalFormatSymbols();
1179:                    decimalFormatSymbols.setDecimalSeparator(point);
1180:                    decimalFormatSymbols.setGroupingSeparator(group);
1181:                    decimalFormatSymbols.setZeroDigit('0');
1182:                }
1183:
1184:                DecimalFormat format = new DecimalFormat(pattern,
1185:                        decimalFormatSymbols);
1186:
1187:                String result = format.format(value);
1188:
1189:                if (point == 0 && decimals > 0) {
1190:                    // no way to get DecimalFormat to output nothing for the point,
1191:                    // so remove it here
1192:                    int i = result.lastIndexOf(point);
1193:
1194:                    return result.substring(0, i)
1195:                            + result.substring(i + 1, result.length());
1196:                } else
1197:                    return result;
1198:            }
1199:
1200:            /**
1201:             * Converts the first character to an integer.
1202:             *
1203:             * @param string the string to be converted
1204:             *
1205:             * @return the integer value
1206:             */
1207:            public static long ord(StringValue string) {
1208:                if (string.length() == 0)
1209:                    return 0;
1210:                else
1211:                    return string.charAt(0);
1212:            }
1213:
1214:            /**
1215:             * Parses the string as a query string.
1216:             *
1217:             * @param env the calling environment
1218:             * @param str the query string
1219:             * @param array the optional result array
1220:             */
1221:            @UsesSymbolTable
1222:            public static Value parse_str(Env env, String str, @Optional
1223:            @Reference
1224:            Value ref) {
1225:                if (str == null)
1226:                    str = "";
1227:
1228:                boolean isRef = ref instanceof  Var;
1229:
1230:                ArrayValue result = null;
1231:
1232:                if (isRef) {
1233:                    result = new ArrayValueImpl();
1234:                    ref.set(result);
1235:                } else if (ref instanceof  ArrayValue) {
1236:                    result = (ArrayValue) ref;
1237:                    isRef = true;
1238:                } else
1239:                    result = new ArrayValueImpl();
1240:
1241:                return StringUtility.parseStr(env, str, result, isRef, env
1242:                        .getHttpInputEncoding());
1243:            }
1244:
1245:            /**
1246:             * Prints the string.
1247:             *
1248:             * @param env the quercus environment
1249:             * @param value the string to print
1250:             */
1251:            public static long print(Env env, Value value) {
1252:                value.print(env);
1253:
1254:                return 1;
1255:            }
1256:
1257:            /**
1258:             * Escapes meta characters.
1259:             *
1260:             * @param string the string to be quoted
1261:             *
1262:             * @return the quoted
1263:             */
1264:            public static Value quotemeta(StringValue string) {
1265:                int len = string.length();
1266:
1267:                StringValue sb = string.createStringBuilder(len * 5 / 4);
1268:
1269:                for (int i = 0; i < len; i++) {
1270:                    char ch = string.charAt(i);
1271:
1272:                    switch (ch) {
1273:                    case '.':
1274:                    case '\\':
1275:                    case '+':
1276:                    case '*':
1277:                    case '?':
1278:                    case '[':
1279:                    case '^':
1280:                    case ']':
1281:                    case '(':
1282:                    case ')':
1283:                    case '$':
1284:                        sb.append("\\");
1285:                        sb.append(ch);
1286:                        break;
1287:                    default:
1288:                        sb.append(ch);
1289:                        break;
1290:                    }
1291:                }
1292:
1293:                return sb;
1294:            }
1295:
1296:            /**
1297:             * Converts a RFC2045 quoted printable string to a string.
1298:             */
1299:            // XXX: i18n
1300:            public static String quoted_printable_decode(String str) {
1301:                if (str == null)
1302:                    str = "";
1303:
1304:                StringBuilder sb = new StringBuilder();
1305:
1306:                int length = str.length();
1307:
1308:                for (int i = 0; i < length; i++) {
1309:                    char ch = str.charAt(i);
1310:
1311:                    if (33 <= ch && ch <= 60)
1312:                        sb.append(ch);
1313:                    else if (62 <= ch && ch <= 126)
1314:                        sb.append(ch);
1315:                    else if (ch == ' ' || ch == '\t') {
1316:                        if (i + 1 < str.length()
1317:                                && (str.charAt(i + 1) == '\r' || str
1318:                                        .charAt(i + 1) == '\n')) {
1319:                            sb.append('=');
1320:                            sb.append(toUpperHexChar(ch >> 4));
1321:                            sb.append(toUpperHexChar(ch));
1322:                        } else
1323:                            sb.append(ch);
1324:                    } else if (ch == '\r' || ch == '\n') {
1325:                        sb.append(ch);
1326:                    } else {
1327:                        sb.append('=');
1328:                        sb.append(toUpperHexChar(ch >> 4));
1329:                        sb.append(toUpperHexChar(ch));
1330:                    }
1331:                }
1332:
1333:                return sb.toString();
1334:            }
1335:
1336:            private static final boolean[] TRIM_WHITESPACE = new boolean[256];
1337:
1338:            static {
1339:                TRIM_WHITESPACE['\0'] = true;
1340:                TRIM_WHITESPACE['\b'] = true;
1341:                TRIM_WHITESPACE[' '] = true;
1342:                TRIM_WHITESPACE['\t'] = true;
1343:                TRIM_WHITESPACE['\r'] = true;
1344:                TRIM_WHITESPACE['\n'] = true;
1345:            }
1346:
1347:            /**
1348:             * Removes leading whitespace.
1349:             *
1350:             * @param string the string to be trimmed
1351:             * @param characters optional set of characters to trim
1352:             * @return the trimmed string
1353:             */
1354:            public static StringValue ltrim(Env env, StringValue string,
1355:                    @Optional
1356:                    String characters) {
1357:                if (characters == null)
1358:                    characters = "";
1359:
1360:                boolean[] trim;
1361:
1362:                if (characters.equals(""))
1363:                    trim = TRIM_WHITESPACE;
1364:                else
1365:                    trim = parseCharsetBitmap(characters);
1366:
1367:                for (int i = 0; i < string.length(); i++) {
1368:                    char ch = string.charAt(i);
1369:
1370:                    if (ch >= 256 || !trim[ch]) {
1371:                        if (i == 0)
1372:                            return string;
1373:                        else
1374:                            return string.substring(i);
1375:                    }
1376:                }
1377:
1378:                return env.createEmptyString();
1379:            }
1380:
1381:            /**
1382:             * Removes trailing whitespace.
1383:             *
1384:             * @param env the quercus environment
1385:             * @param string the string to be trimmed
1386:             * @param characters optional set of characters to trim
1387:             * @return the trimmed string
1388:             */
1389:            public static StringValue rtrim(Env env, StringValue string,
1390:                    @Optional
1391:                    String characters) {
1392:                if (characters == null)
1393:                    characters = "";
1394:
1395:                boolean[] trim;
1396:
1397:                if (characters.equals(""))
1398:                    trim = TRIM_WHITESPACE;
1399:                else
1400:                    trim = parseCharsetBitmap(characters);
1401:
1402:                for (int i = string.length() - 1; i >= 0; i--) {
1403:                    char ch = string.charAt(i);
1404:
1405:                    if (ch >= 256 || !trim[ch]) {
1406:                        if (i == string.length())
1407:                            return string;
1408:                        else
1409:                            return (StringValue) string.subSequence(0, i + 1);
1410:                    }
1411:                }
1412:
1413:                return env.createEmptyString();
1414:            }
1415:
1416:            /**
1417:             * Sets locale configuration.
1418:             */
1419:            public static Value setlocale(Env env, int category,
1420:                    Value localeArg, Value[] fallback) {
1421:                LocaleInfo localeInfo = env.getLocaleInfo();
1422:
1423:                if (localeArg instanceof  ArrayValue) {
1424:                    for (Value value : ((ArrayValue) localeArg).values()) {
1425:                        Locale locale = setLocale(localeInfo, category, value
1426:                                .toString());
1427:
1428:                        if (locale != null)
1429:                            return env.createString(locale.toString());
1430:                    }
1431:                } else {
1432:                    Locale locale = setLocale(localeInfo, category, localeArg
1433:                            .toString());
1434:
1435:                    if (locale != null)
1436:                        return env.createString(locale.toString());
1437:                }
1438:
1439:                for (int i = 0; i < fallback.length; i++) {
1440:                    Locale locale = setLocale(localeInfo, category, fallback[i]
1441:                            .toString());
1442:
1443:                    if (locale != null)
1444:                        return env.createString(locale.toString());
1445:                }
1446:
1447:                return BooleanValue.FALSE;
1448:            }
1449:
1450:            /**
1451:             * Sets locale configuration.
1452:             */
1453:            private static Locale setLocale(LocaleInfo localeInfo,
1454:                    int category, String localeName) {
1455:                String language;
1456:                String country;
1457:                String variant;
1458:
1459:                int p = localeName.indexOf('_');
1460:                int p1 = localeName.indexOf('-');
1461:
1462:                if (p1 > 0 && (p1 < p || p < 0))
1463:                    p = p1;
1464:
1465:                Locale locale;
1466:
1467:                if (p > 0) {
1468:                    language = localeName.substring(0, p);
1469:
1470:                    int q = localeName.indexOf('-', p + 1);
1471:                    int q1 = localeName.indexOf('.', p + 1);
1472:                    // XXX: '.' should be charset?
1473:
1474:                    if (q1 > 0 && (q1 < q || q < 0))
1475:                        q = q1;
1476:
1477:                    q1 = localeName.indexOf('@', p + 1);
1478:                    // XXX: '@' is ??
1479:
1480:                    if (q1 > 0 && (q1 < q || q < 0))
1481:                        q = q1;
1482:
1483:                    q1 = localeName.indexOf('_', p + 1);
1484:
1485:                    if (q1 > 0 && (q1 < q || q < 0))
1486:                        q = q1;
1487:
1488:                    if (q > 0) {
1489:                        country = localeName.substring(p + 1, q);
1490:                        variant = localeName.substring(q + 1);
1491:
1492:                        locale = new Locale(language, country, variant);
1493:                    } else {
1494:                        country = localeName.substring(p + 1);
1495:
1496:                        locale = new Locale(language, country);
1497:                    }
1498:                } else
1499:                    locale = new Locale(localeName);
1500:
1501:                if (!isValidLocale(locale))
1502:                    return null;
1503:
1504:                switch (category) {
1505:                case LC_ALL:
1506:                    localeInfo.setAll(locale);
1507:                    return localeInfo.getMessages();
1508:                case LC_COLLATE:
1509:                    localeInfo.setCollate(locale);
1510:                    return localeInfo.getCollate();
1511:                case LC_CTYPE:
1512:                    localeInfo.setCtype(locale);
1513:                    return localeInfo.getCtype();
1514:                case LC_MONETARY:
1515:                    localeInfo.setMonetary(locale);
1516:                    return localeInfo.getMonetary();
1517:                case LC_NUMERIC:
1518:                    localeInfo.setNumeric(locale);
1519:                    return localeInfo.getNumeric();
1520:                case LC_TIME:
1521:                    localeInfo.setTime(locale);
1522:                    return localeInfo.getTime();
1523:                case LC_MESSAGES:
1524:                    localeInfo.setMessages(locale);
1525:                    return localeInfo.getMessages();
1526:                default:
1527:                    return null;
1528:                }
1529:            }
1530:
1531:            /**
1532:             * Returns true if the locale is supported.
1533:             */
1534:            private static boolean isValidLocale(Locale locale) {
1535:                Locale[] validLocales = Locale.getAvailableLocales();
1536:
1537:                for (int i = 0; i < validLocales.length; i++) {
1538:                    if (validLocales[i].equals(locale)) {
1539:                        return true;
1540:                    }
1541:                }
1542:
1543:                return false;
1544:            }
1545:
1546:            /**
1547:             * Gets locale-specific symbols.
1548:             */
1549:            public static ArrayValue localeconv(Env env) {
1550:                ArrayValueImpl array = new ArrayValueImpl();
1551:
1552:                Locale money = env.getLocaleInfo().getMonetary();
1553:
1554:                DecimalFormatSymbols decimal = new DecimalFormatSymbols(money);
1555:                Currency currency = NumberFormat.getInstance(money)
1556:                        .getCurrency();
1557:
1558:                array.put(env.createString("decimal_point"), env
1559:                        .createString(decimal.getDecimalSeparator()));
1560:                array.put(env.createString("thousands_sep"), env
1561:                        .createString(decimal.getGroupingSeparator()));
1562:                //array.put("grouping", "");
1563:                array
1564:                        .put(env.createString("int_curr_symbol"), env
1565:                                .createString(decimal
1566:                                        .getInternationalCurrencySymbol()));
1567:                array.put(env.createString("currency_symbol"), env
1568:                        .createString(decimal.getCurrencySymbol()));
1569:                array.put(env.createString("mon_decimal_point"), env
1570:                        .createString(decimal.getMonetaryDecimalSeparator()));
1571:                array.put(env.createString("mon_thousands_sep"), env
1572:                        .createString(decimal.getGroupingSeparator()));
1573:                //array.put("mon_grouping", "");
1574:                array.put(env.createString("positive_sign"), env
1575:                        .createEmptyString());
1576:                array.put(env.createString("negative_sign"), env
1577:                        .createString(decimal.getMinusSign()));
1578:                array.put(env.createString("int_frac_digits"), LongValue
1579:                        .create(currency.getDefaultFractionDigits()));
1580:                array.put(env.createString("frac_digits"), LongValue
1581:                        .create(currency.getDefaultFractionDigits()));
1582:                //array.put("p_cs_precedes", "");
1583:                //array.put("p_sep_by_space", "");
1584:                //array.put("n_cs_precedes", "");
1585:                //array.put("n_sep_by_space", "");
1586:                //array.put("p_sign_posn", "");
1587:                //array.put("n_sign_posn", "");
1588:
1589:                return array;
1590:            }
1591:
1592:            /**
1593:             * returns the md5 hash
1594:             *
1595:             * @param source the string
1596:             * @param rawOutput if true, return the raw binary
1597:             *
1598:             * @return a string of imploded values
1599:             */
1600:            public static String sha1(String source, @Optional
1601:            boolean rawOutput) {
1602:                if (source == null)
1603:                    source = "";
1604:
1605:                try {
1606:                    MessageDigest md = MessageDigest.getInstance("SHA1");
1607:
1608:                    // XXX: iso-8859-1
1609:
1610:                    for (int i = 0; i < source.length(); i++) {
1611:                        char ch = source.charAt(i);
1612:
1613:                        md.update((byte) ch);
1614:                    }
1615:
1616:                    byte[] digest = md.digest();
1617:
1618:                    StringBuilder sb = new StringBuilder();
1619:                    for (int i = 0; i < digest.length; i++) {
1620:                        int d1 = (digest[i] >> 4) & 0xf;
1621:                        int d2 = (digest[i] & 0xf);
1622:
1623:                        sb.append(toHexChar(d1));
1624:                        sb.append(toHexChar(d2));
1625:                    }
1626:
1627:                    return sb.toString();
1628:                } catch (Exception e) {
1629:                    throw new QuercusException(e);
1630:                }
1631:            }
1632:
1633:            /**
1634:             * returns the md5 hash
1635:             *
1636:             * @param source the string
1637:             * @param rawOutput if true, return the raw binary
1638:             *
1639:             * @return a string of imploded values
1640:             */
1641:            public static Value sha1_file(Env env, Path source, @Optional
1642:            boolean rawOutput) {
1643:                try {
1644:                    MessageDigest md = MessageDigest.getInstance("SHA1");
1645:                    InputStream is = null;
1646:
1647:                    try {
1648:                        is = source.openRead();
1649:                        int d;
1650:
1651:                        while ((d = is.read()) >= 0) {
1652:                            md.update((byte) d);
1653:                        }
1654:
1655:                        return digestToString(env, md.digest());
1656:                    } catch (IOException e) {
1657:                        log.log(Level.FINE, e.toString(), e);
1658:
1659:                        return BooleanValue.FALSE;
1660:                    } finally {
1661:                        try {
1662:                            if (is != null)
1663:                                is.close();
1664:                        } catch (IOException e) {
1665:                        }
1666:                    }
1667:                } catch (Exception e) {
1668:                    throw new QuercusException(e);
1669:                }
1670:            }
1671:
1672:            /**
1673:             * scans a string
1674:             *
1675:             * @param format the format string
1676:             * @param args the format arguments
1677:             *
1678:             * @return the formatted string
1679:             */
1680:            public static Value sscanf(Env env, StringValue string,
1681:                    StringValue format, @Optional
1682:                    @Reference
1683:                    Value[] args) {
1684:                int fmtLen = format.length();
1685:                int strlen = string.length();
1686:
1687:                int sIndex = 0;
1688:                int fIndex = 0;
1689:
1690:                boolean isAssign = args.length != 0;
1691:                int argIndex = 0;
1692:
1693:                ArrayValue array = new ArrayValueImpl();
1694:
1695:                while (fIndex < fmtLen) {
1696:                    char ch = format.charAt(fIndex++);
1697:
1698:                    if (isWhitespace(ch)) {
1699:                        for (; (fIndex < fmtLen && isWhitespace(ch = format
1700:                                .charAt(fIndex))); fIndex++) {
1701:                        }
1702:
1703:                        ch = string.charAt(sIndex);
1704:                        if (!isWhitespace(ch)) {
1705:                            // XXX: return false?
1706:                            return sscanfReturn(env, array, args, argIndex,
1707:                                    isAssign);
1708:                        }
1709:
1710:                        for (sIndex++; sIndex < strlen
1711:                                && isWhitespace(string.charAt(sIndex)); sIndex++) {
1712:                        }
1713:                    } else if (ch == '%') {
1714:                        int maxLen = -1;
1715:
1716:                        loop: while (fIndex < fmtLen) {
1717:                            ch = format.charAt(fIndex++);
1718:
1719:                            if (sIndex >= strlen) {
1720:                                array.append(NullValue.NULL);
1721:                                break loop;
1722:                            }
1723:
1724:                            Value obj;
1725:
1726:                            if (isAssign) {
1727:                                if (argIndex < args.length)
1728:                                    obj = args[argIndex++];
1729:                                else {
1730:                                    env.warning(L
1731:                                            .l("not enough vars passed in"));
1732:                                    break loop;
1733:                                }
1734:                            } else
1735:                                obj = array;
1736:
1737:                            switch (ch) {
1738:                            case '%':
1739:                                if (string.charAt(sIndex) != '%')
1740:                                    return sscanfReturn(env, array, args,
1741:                                            argIndex, isAssign);
1742:                                else
1743:                                    break loop;
1744:
1745:                            case '0':
1746:                            case '1':
1747:                            case '2':
1748:                            case '3':
1749:                            case '4':
1750:                            case '5':
1751:                            case '6':
1752:                            case '7':
1753:                            case '8':
1754:                            case '9':
1755:                                if (maxLen < 0)
1756:                                    maxLen = 0;
1757:
1758:                                maxLen = 10 * maxLen + ch - '0';
1759:                                break;
1760:
1761:                            case 's':
1762:                                sIndex = sscanfString(string, sIndex, maxLen,
1763:                                        obj, isAssign);
1764:                                break loop;
1765:
1766:                            case 'c':
1767:                                if (maxLen < 0)
1768:                                    maxLen = 1;
1769:
1770:                                sIndex = sscanfString(string, sIndex, maxLen,
1771:                                        obj, isAssign);
1772:                                break loop;
1773:
1774:                            case 'd':
1775:                                sIndex = sscanfInteger(string, sIndex, maxLen,
1776:                                        obj, isAssign, 10, false);
1777:                                break loop;
1778:
1779:                            case 'u':
1780:                                sIndex = sscanfInteger(string, sIndex, maxLen,
1781:                                        obj, isAssign, 10, true);
1782:                                break loop;
1783:
1784:                            case 'o':
1785:                                sIndex = sscanfInteger(string, sIndex, maxLen,
1786:                                        obj, isAssign, 8, false);
1787:                                break loop;
1788:
1789:                            case 'x':
1790:                            case 'X':
1791:                                sIndex = sscanfHex(string, sIndex, maxLen, obj,
1792:                                        isAssign);
1793:                                break loop;
1794:
1795:                            case 'e':
1796:                            case 'f':
1797:                                sIndex = sscanfScientific(string, sIndex,
1798:                                        maxLen, obj, isAssign);
1799:                                break loop;
1800:
1801:                            default:
1802:                                log.fine(L.l("'{0}' is a bad sscanf string.",
1803:                                        format));
1804:                                env.warning(L
1805:                                        .l("'{0}' is a bad sscanf string.",
1806:                                                format));
1807:
1808:                                return isAssign ? LongValue.create(argIndex)
1809:                                        : array;
1810:                            }
1811:                        }
1812:                    } else if (ch == string.charAt(sIndex)) {
1813:                        sIndex++;
1814:                    } else
1815:                        return sscanfReturn(env, array, args, argIndex,
1816:                                isAssign);
1817:                }
1818:
1819:                return sscanfReturn(env, array, args, argIndex, isAssign);
1820:            }
1821:
1822:            private static Value sscanfReturn(Env env, ArrayValue array,
1823:                    Value[] args, int argIndex, boolean isAssign) {
1824:                if (isAssign) {
1825:                    if (argIndex != args.length)
1826:                        env.warning(L.l(
1827:                                "{0} vars passed in but saw only {1} '%' args",
1828:                                args.length, argIndex));
1829:
1830:                    return LongValue.create(argIndex);
1831:                } else {
1832:                    return array;
1833:                }
1834:            }
1835:
1836:            /**
1837:             * Scans a string with a given length.
1838:             */
1839:            private static int sscanfString(StringValue string, int sIndex,
1840:                    int maxLen, Value obj, boolean isAssignment) {
1841:                int strlen = string.length();
1842:
1843:                if (maxLen < 0)
1844:                    maxLen = Integer.MAX_VALUE;
1845:
1846:                StringValue sb = string.createStringBuilder();
1847:
1848:                for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
1849:                    char ch = string.charAt(sIndex);
1850:
1851:                    if (!isWhitespace(ch))
1852:                        sb.append(ch);
1853:                    else
1854:                        break;
1855:                }
1856:
1857:                sscanfPut(obj, sb, isAssignment);
1858:
1859:                return sIndex;
1860:            }
1861:
1862:            private static void sscanfPut(Value obj, Value val,
1863:                    boolean isAssignment) {
1864:                if (isAssignment)
1865:                    obj.set(val);
1866:                else
1867:                    obj.put(val);
1868:            }
1869:
1870:            /**
1871:             * Scans a integer with a given length.
1872:             */
1873:            private static int sscanfInteger(StringValue string, int sIndex,
1874:                    int maxLen, Value obj, boolean isAssign, int base,
1875:                    boolean isUnsigned) {
1876:                int strlen = string.length();
1877:
1878:                if (maxLen < 0)
1879:                    maxLen = Integer.MAX_VALUE;
1880:
1881:                int val = 0;
1882:                int sign = 1;
1883:                boolean isNotMatched = true;
1884:
1885:                if (sIndex < strlen) {
1886:                    char ch = string.charAt(sIndex);
1887:
1888:                    if (ch == '+') {
1889:                        sIndex++;
1890:                        maxLen--;
1891:                    } else if (ch == '-') {
1892:                        sign = -1;
1893:
1894:                        sIndex++;
1895:                        maxLen--;
1896:                    }
1897:                }
1898:
1899:                int topRange = base + '0';
1900:
1901:                for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
1902:                    char ch = string.charAt(sIndex);
1903:
1904:                    if ('0' <= ch && ch < topRange) {
1905:                        val = val * base + ch - '0';
1906:                        isNotMatched = false;
1907:                    } else if (isNotMatched) {
1908:                        sscanfPut(obj, NullValue.NULL, isAssign);
1909:                        return sIndex;
1910:                    } else
1911:                        break;
1912:                }
1913:
1914:                if (isUnsigned) {
1915:                    if (sign == -1 && val != 0)
1916:                        sscanfPut(obj, StringValue
1917:                                .create(0xFFFFFFFFL - val + 1), isAssign);
1918:                    else
1919:                        sscanfPut(obj, LongValue.create(val), isAssign);
1920:                } else
1921:                    sscanfPut(obj, LongValue.create(val * sign), isAssign);
1922:
1923:                return sIndex;
1924:            }
1925:
1926:            /**
1927:             * Scans a integer with a given length.
1928:             */
1929:            private static int sscanfHex(StringValue string, int sIndex,
1930:                    int maxLen, Value obj, boolean isAssign) {
1931:                int strlen = string.length();
1932:
1933:                if (maxLen < 0)
1934:                    maxLen = Integer.MAX_VALUE;
1935:
1936:                int val = 0;
1937:                int sign = 1;
1938:                boolean isMatched = false;
1939:
1940:                if (sIndex < strlen) {
1941:                    char ch = string.charAt(sIndex);
1942:
1943:                    if (ch == '+') {
1944:                        sIndex++;
1945:                        maxLen--;
1946:                    } else if (ch == '-') {
1947:                        sign = -1;
1948:
1949:                        sIndex++;
1950:                        maxLen--;
1951:                    }
1952:                }
1953:
1954:                for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
1955:                    char ch = string.charAt(sIndex);
1956:
1957:                    if ('0' <= ch && ch <= '9') {
1958:                        val = val * 16 + ch - '0';
1959:                        isMatched = true;
1960:                    } else if ('a' <= ch && ch <= 'f') {
1961:                        val = val * 16 + ch - 'a' + 10;
1962:                        isMatched = true;
1963:                    } else if ('A' <= ch && ch <= 'F') {
1964:                        val = val * 16 + ch - 'A' + 10;
1965:                        isMatched = true;
1966:                    } else if (!isMatched) {
1967:                        sscanfPut(obj, NullValue.NULL, isAssign);
1968:                        return sIndex;
1969:                    } else
1970:                        break;
1971:                }
1972:
1973:                sscanfPut(obj, LongValue.create(val * sign), isAssign);
1974:
1975:                return sIndex;
1976:            }
1977:
1978:            /**
1979:             * Scans a integer with a given length.
1980:             */
1981:            private static int sscanfScientific(StringValue s, int i,
1982:                    int maxLen, Value obj, boolean isAssign) {
1983:                if (maxLen < 0)
1984:                    maxLen = Integer.MAX_VALUE;
1985:
1986:                int start = i;
1987:                int len = s.length();
1988:                int ch = 0;
1989:
1990:                if (i < len && maxLen > 0
1991:                        && ((ch = s.charAt(i)) == '+' || ch == '-')) {
1992:                    i++;
1993:                    maxLen--;
1994:                }
1995:
1996:                for (; i < len && maxLen > 0 && '0' <= (ch = s.charAt(i))
1997:                        && ch <= '9'; i++) {
1998:                    maxLen--;
1999:                }
2000:
2001:                if (ch == '.') {
2002:                    maxLen--;
2003:
2004:                    for (i++; i < len && maxLen > 0
2005:                            && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) {
2006:                        maxLen--;
2007:                    }
2008:                }
2009:
2010:                if (ch == 'e' || ch == 'E') {
2011:                    maxLen--;
2012:
2013:                    int e = i++;
2014:
2015:                    if (start == e) {
2016:                        sscanfPut(obj, NullValue.NULL, isAssign);
2017:                        return start;
2018:                    }
2019:
2020:                    if (i < len && maxLen > 0 && (ch = s.charAt(i)) == '+'
2021:                            || ch == '-') {
2022:                        i++;
2023:                        maxLen--;
2024:                    }
2025:
2026:                    for (; i < len && maxLen > 0 && '0' <= (ch = s.charAt(i))
2027:                            && ch <= '9'; i++) {
2028:                        maxLen--;
2029:                    }
2030:
2031:                    if (i == e + 1)
2032:                        i = e;
2033:                }
2034:
2035:                double val;
2036:
2037:                if (i == 0)
2038:                    val = 0;
2039:                else
2040:                    val = Double.parseDouble(s.substring(start, i).toString());
2041:
2042:                sscanfPut(obj, DoubleValue.create(val), isAssign);
2043:
2044:                return i;
2045:            }
2046:
2047:            /**
2048:             * print to the output with a formatter
2049:             *
2050:             * @param env the quercus environment
2051:             * @param format the format string
2052:             * @param args the format arguments
2053:             *
2054:             * @return the formatted string
2055:             */
2056:            public static int printf(Env env, StringValue format, Value[] args) {
2057:                Value str = sprintf(format, args);
2058:
2059:                str.print(env);
2060:
2061:                return str.length();
2062:            }
2063:
2064:            private static final char[] SOUNDEX_VALUES = "01230120022455012623010202"
2065:                    .toCharArray();
2066:
2067:            public static Value soundex(StringValue string) {
2068:                int length = string.length();
2069:
2070:                if (length == 0)
2071:                    return BooleanValue.FALSE;
2072:
2073:                StringValue result = string.createStringBuilder();
2074:
2075:                int count = 0;
2076:                char lastCode = 0;
2077:
2078:                for (int i = 0; i < length && count < 4; i++) {
2079:                    char ch = toUpperCase(string.charAt(i));
2080:
2081:                    if ('A' <= ch && ch <= 'Z') {
2082:                        char code = SOUNDEX_VALUES[ch - 'A'];
2083:
2084:                        if (count == 0) {
2085:                            result.append(ch);
2086:                            count++;
2087:                        } else if (code != '0' && code != lastCode) {
2088:                            result.append(code);
2089:                            count++;
2090:                        }
2091:
2092:                        lastCode = code;
2093:                    }
2094:                }
2095:
2096:                for (; count < 4; count++) {
2097:                    result.append('0');
2098:                }
2099:
2100:                return result;
2101:            }
2102:
2103:            /**
2104:             * Print to a string with a formatter
2105:             *
2106:             * @param format the format string
2107:             * @param args the format arguments
2108:             *
2109:             * @return the formatted string
2110:             */
2111:            public static Value sprintf(StringValue format, Value[] args) {
2112:                ArrayList<PrintfSegment> segments = parsePrintfFormat(format);
2113:
2114:                StringValue sb = format.createStringBuilder();
2115:
2116:                for (PrintfSegment segment : segments)
2117:                    segment.apply(sb, args);
2118:
2119:                return sb;
2120:            }
2121:
2122:            private static ArrayList<PrintfSegment> parsePrintfFormat(
2123:                    StringValue format) {
2124:                ArrayList<PrintfSegment> segments = new ArrayList<PrintfSegment>();
2125:
2126:                StringBuilder sb = new StringBuilder();
2127:                StringBuilder flags = new StringBuilder();
2128:
2129:                int length = format.length();
2130:                int index = 0;
2131:
2132:                for (int i = 0; i < length; i++) {
2133:                    char ch = format.charAt(i);
2134:
2135:                    if (i + 1 < length && ch == '%') {
2136:                        // The C printf silently ignores invalid flags, so we need to
2137:                        // remove them if present.
2138:
2139:                        sb.append(ch);
2140:
2141:                        boolean isLeft = false;
2142:                        boolean isAlt = false;
2143:                        boolean isZero = false;
2144:
2145:                        flags.setLength(0);
2146:
2147:                        int j = i + 1;
2148:
2149:                        loop: for (; j < length; j++) {
2150:                            ch = format.charAt(j);
2151:
2152:                            switch (ch) {
2153:                            case '-':
2154:                                isLeft = true;
2155:                                break;
2156:                            case '#':
2157:                                isAlt = true;
2158:                                break;
2159:                            case '0':
2160:                                isZero = true;
2161:                                flags.append(ch);
2162:                                break;
2163:                            case '+':
2164:                            case ' ':
2165:                            case ',':
2166:                            case '(':
2167:                                flags.append(ch);
2168:                                break;
2169:                            default:
2170:                                break loop;
2171:                            }
2172:                        }
2173:
2174:                        int head = j;
2175:                        loop: for (; j < length; j++) {
2176:                            ch = format.charAt(j);
2177:
2178:                            switch (ch) {
2179:                            case '%':
2180:                                i = j;
2181:                                segments.add(new TextPrintfSegment(sb));
2182:                                sb.setLength(0);
2183:                                break loop;
2184:
2185:                            case '0':
2186:                            case '1':
2187:                            case '2':
2188:                            case '3':
2189:                            case '4':
2190:                            case '5':
2191:                            case '6':
2192:                            case '7':
2193:                            case '8':
2194:                            case '9':
2195:                            case '.':
2196:                            case '$':
2197:                                break;
2198:
2199:                            case 'b':
2200:                            case 'B':
2201:                                if (isLeft)
2202:                                    sb.append('-');
2203:                                if (isAlt)
2204:                                    sb.append('#');
2205:                                sb.append(format, head, j);
2206:                                sb.append(ch);
2207:                                i = j;
2208:                                break loop;
2209:
2210:                            case 's':
2211:                            case 'S':
2212:                                sb.setLength(sb.length() - 1);
2213:                                segments
2214:                                        .add(new StringPrintfSegment(sb, isLeft
2215:                                                || isAlt, isZero, ch == 'S',
2216:                                                format.substring(head, j)
2217:                                                        .toString(), index++));
2218:                                sb.setLength(0);
2219:                                i = j;
2220:                                break loop;
2221:
2222:                            case 'c':
2223:                            case 'C':
2224:                                sb.setLength(sb.length() - 1);
2225:                                segments
2226:                                        .add(new CharPrintfSegment(sb, isLeft
2227:                                                || isAlt, isZero, ch == 'C',
2228:                                                format.substring(head, j)
2229:                                                        .toString(), index++));
2230:                                sb.setLength(0);
2231:                                i = j;
2232:                                break loop;
2233:
2234:                            case 'i':
2235:                            case 'u':
2236:                                ch = 'd';
2237:                            case 'd':
2238:                            case 'x':
2239:                            case 'o':
2240:                            case 'X':
2241:                                sb.setLength(sb.length() - 1);
2242:                                if (sb.length() > 0)
2243:                                    segments.add(new TextPrintfSegment(sb));
2244:                                sb.setLength(0);
2245:
2246:                                if (isLeft)
2247:                                    sb.append('-');
2248:                                if (isAlt)
2249:                                    sb.append('#');
2250:                                sb.append(flags);
2251:                                sb.append(format, head, j);
2252:                                sb.append(ch);
2253:
2254:                                segments.add(LongPrintfSegment.create(sb
2255:                                        .toString(), index++));
2256:                                sb.setLength(0);
2257:                                i = j;
2258:                                break loop;
2259:
2260:                            case 'e':
2261:                            case 'E':
2262:                            case 'f':
2263:                            case 'g':
2264:                            case 'G':
2265:                                sb.setLength(sb.length() - 1);
2266:                                if (sb.length() > 0)
2267:                                    segments.add(new TextPrintfSegment(sb));
2268:                                sb.setLength(0);
2269:
2270:                                if (isLeft)
2271:                                    sb.append('-');
2272:                                if (isAlt)
2273:                                    sb.append('#');
2274:                                sb.append(flags);
2275:                                sb.append(format, head, j);
2276:                                sb.append(ch);
2277:
2278:                                segments.add(new DoublePrintfSegment(sb
2279:                                        .toString(), index++));
2280:                                sb.setLength(0);
2281:                                i = j;
2282:                                break loop;
2283:
2284:                            default:
2285:                                if (isLeft)
2286:                                    sb.append('-');
2287:                                if (isAlt)
2288:                                    sb.append('#');
2289:                                sb.append(flags);
2290:                                sb.append(format, head, j);
2291:                                sb.append(ch);
2292:                                i = j;
2293:                                break loop;
2294:                            }
2295:                        }
2296:                    } else
2297:                        sb.append(ch);
2298:                }
2299:
2300:                if (sb.length() > 0)
2301:                    segments.add(new TextPrintfSegment(sb));
2302:
2303:                return segments;
2304:            }
2305:
2306:            /**
2307:             * replaces substrings.
2308:             *
2309:             * @param search search string
2310:             * @param replace replacement string
2311:             * @param subject replacement
2312:             * @param count return value
2313:             */
2314:            public static Value str_ireplace(Env env, Value search,
2315:                    Value replace, Value subject, @Reference
2316:                    @Optional
2317:                    Value count) {
2318:                return strReplace(env, search, replace, subject, count, true);
2319:            }
2320:
2321:            /**
2322:             * Pads strings
2323:             *
2324:             * @param string string
2325:             * @param length length
2326:             * @param pad padding string
2327:             * @param type padding type
2328:             */
2329:            public static StringValue str_pad(StringValue string, int length,
2330:                    @Optional("' '")
2331:                    String pad, @Optional("STR_PAD_RIGHT")
2332:                    int type) {
2333:                int strLen = string.length();
2334:                int padLen = length - strLen;
2335:
2336:                if (padLen <= 0)
2337:                    return string;
2338:
2339:                if (pad == null || pad.length() == 0)
2340:                    pad = " ";
2341:
2342:                int leftPad = 0;
2343:                int rightPad = 0;
2344:
2345:                switch (type) {
2346:                case STR_PAD_LEFT:
2347:                    leftPad = padLen;
2348:                    break;
2349:                case STR_PAD_RIGHT:
2350:                default:
2351:                    rightPad = padLen;
2352:                    break;
2353:                case STR_PAD_BOTH:
2354:                    leftPad = padLen / 2;
2355:                    rightPad = padLen - leftPad;
2356:                    break;
2357:                }
2358:
2359:                int padStringLen = pad.length();
2360:
2361:                StringValue sb = string.createStringBuilder(string.length()
2362:                        + padLen);
2363:
2364:                for (int i = 0; i < leftPad; i++)
2365:                    sb.append(pad.charAt(i % padStringLen));
2366:
2367:                sb = sb.append(string);
2368:
2369:                for (int i = 0; i < rightPad; i++)
2370:                    sb.append(pad.charAt(i % padStringLen));
2371:
2372:                return sb;
2373:            }
2374:
2375:            /**
2376:             * repeats a string
2377:             *
2378:             * @param string string to repeat
2379:             * @param count number of times to repeat
2380:             */
2381:            public static Value str_repeat(StringValue string, int count) {
2382:                StringValue sb = string.createStringBuilder(count
2383:                        * string.length());
2384:
2385:                for (int i = 0; i < count; i++)
2386:                    sb = sb.append(string);
2387:
2388:                return sb;
2389:            }
2390:
2391:            /**
2392:             * replaces substrings.
2393:             *
2394:             * @param search search string
2395:             * @param replace replacement string
2396:             * @param subject replacement
2397:             * @param count return value
2398:             */
2399:            public static Value str_replace(Env env, Value search,
2400:                    Value replace, Value subject, @Reference
2401:                    @Optional
2402:                    Value count) {
2403:                return strReplace(env, search, replace, subject, count, false);
2404:            }
2405:
2406:            /**
2407:             * replaces substrings.
2408:             *
2409:             * @param search search string
2410:             * @param replace replacement string
2411:             * @param subject replacement
2412:             * @param count return value
2413:             */
2414:            private static Value strReplace(Env env, Value search,
2415:                    Value replace, Value subject, @Reference
2416:                    @Optional
2417:                    Value count, boolean isInsensitive) {
2418:                count.set(LongValue.ZERO);
2419:
2420:                if (subject.isNull())
2421:                    return env.createEmptyString();
2422:
2423:                if (search.isNull())
2424:                    return subject;
2425:
2426:                if (subject instanceof  ArrayValue) {
2427:                    ArrayValue subjectArray = (ArrayValue) subject;
2428:                    ArrayValue resultArray = new ArrayValueImpl();
2429:
2430:                    for (Map.Entry<Value, Value> entry : subjectArray
2431:                            .entrySet()) {
2432:                        Value result = strReplaceImpl(env, search, replace,
2433:                                entry.getValue().toStringValue(), count,
2434:                                isInsensitive);
2435:
2436:                        resultArray.append(entry.getKey(), result);
2437:                    }
2438:
2439:                    return resultArray;
2440:                } else {
2441:                    StringValue subjectString = subject.toStringValue();
2442:
2443:                    if (subjectString.length() == 0)
2444:                        return env.createEmptyString();
2445:
2446:                    return strReplaceImpl(env, search, replace, subjectString,
2447:                            count, isInsensitive);
2448:                }
2449:            }
2450:
2451:            /**
2452:             * replaces substrings.
2453:             *
2454:             * @param search search string
2455:             * @param replace replacement string
2456:             * @param subject replacement
2457:             * @param count return value
2458:             */
2459:            private static Value strReplaceImpl(Env env, Value search,
2460:                    Value replace, StringValue subject, Value count,
2461:                    boolean isInsensitive) {
2462:                if (!search.isArray()) {
2463:                    StringValue searchString = search.toStringValue();
2464:
2465:                    if (searchString.length() == 0)
2466:                        return subject;
2467:
2468:                    if (replace instanceof  ArrayValue) {
2469:                        env.warning(L.l("Array to string conversion"));
2470:                    }
2471:
2472:                    subject = strReplaceImpl(env, searchString, replace
2473:                            .toStringValue(), subject, count, isInsensitive);
2474:                } else if (replace instanceof  ArrayValue) {
2475:                    ArrayValue searchArray = (ArrayValue) search;
2476:                    ArrayValue replaceArray = (ArrayValue) replace;
2477:
2478:                    Iterator<Value> searchIter = searchArray.values()
2479:                            .iterator();
2480:                    Iterator<Value> replaceIter = replaceArray.values()
2481:                            .iterator();
2482:
2483:                    while (searchIter.hasNext()) {
2484:                        Value searchItem = searchIter.next();
2485:                        Value replaceItem = replaceIter.next();
2486:
2487:                        if (replaceItem == null)
2488:                            replaceItem = NullValue.NULL;
2489:
2490:                        subject = strReplaceImpl(env, searchItem
2491:                                .toStringValue(), replaceItem.toStringValue(),
2492:                                subject, count, isInsensitive);
2493:                    }
2494:                } else {
2495:                    ArrayValue searchArray = (ArrayValue) search;
2496:
2497:                    Iterator<Value> searchIter = searchArray.values()
2498:                            .iterator();
2499:
2500:                    while (searchIter.hasNext()) {
2501:                        Value searchItem = searchIter.next();
2502:
2503:                        subject = strReplaceImpl(env, searchItem
2504:                                .toStringValue(), replace.toStringValue(),
2505:                                subject, count, isInsensitive);
2506:                    }
2507:                }
2508:
2509:                return subject;
2510:            }
2511:
2512:            /**
2513:             * replaces substrings.
2514:             *
2515:             * @param search search string
2516:             * @param replace replacement string
2517:             * @param subject replacement
2518:             * @param countV return value
2519:             */
2520:            private static StringValue strReplaceImpl(Env env,
2521:                    StringValue search, StringValue replace,
2522:                    StringValue subject, Value countV, boolean isInsensitive) {
2523:                long count = countV.toLong();
2524:
2525:                int head = 0;
2526:                int next;
2527:
2528:                int searchLen = search.length();
2529:
2530:                StringValue result = null;
2531:
2532:                while ((next = indexOf(subject, search, head, isInsensitive)) >= head) {
2533:                    if (result == null)
2534:                        result = subject.createStringBuilder();
2535:
2536:                    result = result.append(subject, head, next);
2537:                    result = result.append(replace);
2538:
2539:                    if (head < next + searchLen)
2540:                        head = next + searchLen;
2541:                    else
2542:                        head += 1;
2543:
2544:                    count++;
2545:                }
2546:
2547:                if (count != 0) {
2548:                    countV.set(LongValue.create(count));
2549:
2550:                    if (head > 0 && head < subject.length())
2551:                        result = result.append(subject, head, subject.length());
2552:
2553:                    return result;
2554:                } else
2555:                    return subject;
2556:            }
2557:
2558:            /**
2559:             * Returns the next index.
2560:             */
2561:            private static int indexOf(StringValue subject, StringValue match,
2562:                    int head, boolean isInsensitive) {
2563:                if (!isInsensitive)
2564:                    return subject.indexOf(match, head);
2565:                else {
2566:                    int length = subject.length();
2567:                    int matchLen = match.length();
2568:
2569:                    if (matchLen <= 0)
2570:                        return -1;
2571:
2572:                    char ch = Character.toLowerCase(match.charAt(0));
2573:                    loop: for (; head + matchLen <= length; head++) {
2574:                        if (ch == Character.toLowerCase(subject.charAt(head))) {
2575:                            for (int i = 1; i < matchLen; i++) {
2576:                                if (Character.toLowerCase(subject.charAt(head
2577:                                        + i)) != Character.toLowerCase(match
2578:                                        .charAt(i)))
2579:                                    continue loop;
2580:                            }
2581:
2582:                            return head;
2583:                        }
2584:                    }
2585:
2586:                    return -1;
2587:                }
2588:            }
2589:
2590:            /**
2591:             * rot13 conversion
2592:             *
2593:             * @param string string to convert
2594:             */
2595:            public static Value str_rot13(StringValue string) {
2596:                if (string == null)
2597:                    return NullValue.NULL;
2598:
2599:                StringValue sb = string.createStringBuilder(string.length());
2600:
2601:                int len = string.length();
2602:                for (int i = 0; i < len; i++) {
2603:                    char ch = string.charAt(i);
2604:
2605:                    if ('a' <= ch && ch <= 'z') {
2606:                        int off = ch - 'a';
2607:
2608:                        sb.append((char) ('a' + (off + 13) % 26));
2609:                    } else if ('A' <= ch && ch <= 'Z') {
2610:                        int off = ch - 'A';
2611:
2612:                        sb.append((char) ('A' + (off + 13) % 26));
2613:                    } else {
2614:                        sb.append(ch);
2615:                    }
2616:                }
2617:
2618:                return sb;
2619:            }
2620:
2621:            /**
2622:             * shuffles a string
2623:             */
2624:            public static String str_shuffle(String string) {
2625:                if (string == null)
2626:                    string = "";
2627:
2628:                char[] chars = string.toCharArray();
2629:
2630:                int length = chars.length;
2631:
2632:                for (int i = 0; i < length; i++) {
2633:                    int rand = RandomUtil.nextInt(length);
2634:
2635:                    char temp = chars[rand];
2636:                    chars[rand] = chars[i];
2637:                    chars[i] = temp;
2638:                }
2639:
2640:                return new String(chars);
2641:            }
2642:
2643:            /**
2644:             * split into an array
2645:             *
2646:             * @param string string to split
2647:             * @param chunk chunk size
2648:             */
2649:            public static Value str_split(StringValue string, @Optional("1")
2650:            int chunk) {
2651:                ArrayValue array = new ArrayValueImpl();
2652:
2653:                if (string.length() == 0) {
2654:                    array.put(string);
2655:                    return array;
2656:                }
2657:
2658:                int strLen = string.length();
2659:
2660:                for (int i = 0; i < strLen; i += chunk) {
2661:                    Value value;
2662:
2663:                    if (i + chunk <= strLen) {
2664:                        value = string.substring(i, i + chunk);
2665:                    } else {
2666:                        value = string.substring(i);
2667:                    }
2668:
2669:                    array.put(new LongValue(i), value);
2670:                }
2671:
2672:                return array;
2673:            }
2674:
2675:            public static Value str_word_count(StringValue string, @Optional
2676:            int format, @Optional
2677:            String additionalWordCharacters) {
2678:                if (format < 0 || format > 2)
2679:                    return NullValue.NULL;
2680:
2681:                int strlen = string.length();
2682:                boolean isAdditionalWordCharacters = additionalWordCharacters
2683:                        .length() > 0;
2684:
2685:                ArrayValueImpl resultArray = null;
2686:
2687:                if (format > 0)
2688:                    resultArray = new ArrayValueImpl();
2689:
2690:                boolean isBetweenWords = true;
2691:
2692:                int wordCount = 0;
2693:
2694:                int lastWordStart = 0;
2695:
2696:                for (int i = 0; i <= strlen; i++) {
2697:                    boolean isWordCharacter;
2698:
2699:                    if (i < strlen) {
2700:                        int ch = string.charAt(i);
2701:
2702:                        isWordCharacter = Character.isLetter(ch)
2703:                                || ch == '-'
2704:                                || ch == '\''
2705:                                || (isAdditionalWordCharacters && additionalWordCharacters
2706:                                        .indexOf(ch) > -1);
2707:                    } else
2708:                        isWordCharacter = false;
2709:
2710:                    if (isWordCharacter) {
2711:                        if (isBetweenWords) {
2712:                            // starting a word
2713:                            isBetweenWords = false;
2714:
2715:                            lastWordStart = i;
2716:                            wordCount++;
2717:                        }
2718:                    } else {
2719:                        if (!isBetweenWords) {
2720:                            // finished a word
2721:                            isBetweenWords = true;
2722:
2723:                            if (format > 0) {
2724:                                StringValue word = string.substring(
2725:                                        lastWordStart, i);
2726:
2727:                                if (format == 1)
2728:                                    resultArray.append(word);
2729:                                else if (format == 2)
2730:                                    resultArray.put(
2731:                                            new LongValue(lastWordStart), word);
2732:                            }
2733:                        }
2734:                    }
2735:                }
2736:
2737:                if (resultArray == null)
2738:                    return LongValue.create(wordCount);
2739:                else
2740:                    return resultArray;
2741:            }
2742:
2743:            /**
2744:             * Case-insensitive comparison
2745:             *
2746:             * @param a left value
2747:             * @param b right value
2748:             * @return -1, 0, or 1
2749:             */
2750:            public static int strcasecmp(StringValue a, StringValue b) {
2751:                int aLen = a.length();
2752:                int bLen = b.length();
2753:
2754:                for (int i = 0; i < aLen && i < bLen; i++) {
2755:                    char chA = a.charAt(i);
2756:                    char chB = b.charAt(i);
2757:
2758:                    if (chA == chB)
2759:                        continue;
2760:
2761:                    if (Character.isUpperCase(chA))
2762:                        chA = Character.toLowerCase(chA);
2763:
2764:                    if (Character.isUpperCase(chB))
2765:                        chB = Character.toLowerCase(chB);
2766:
2767:                    if (chA == chB)
2768:                        continue;
2769:                    else if (chA < chB)
2770:                        return -1;
2771:                    else
2772:                        return 1;
2773:                }
2774:
2775:                if (aLen == bLen)
2776:                    return 0;
2777:                else if (aLen < bLen)
2778:                    return -1;
2779:                else
2780:                    return 1;
2781:            }
2782:
2783:            /**
2784:             * Case-sensitive comparison
2785:             *
2786:             * @param a left value
2787:             * @param b right value
2788:             * @return -1, 0, or 1
2789:             */
2790:            public static int strcmp(StringValue a, StringValue b) {
2791:                int aLen = a.length();
2792:                int bLen = b.length();
2793:
2794:                for (int i = 0; i < aLen && i < bLen; i++) {
2795:                    char chA = a.charAt(i);
2796:                    char chB = b.charAt(i);
2797:
2798:                    if (chA == chB)
2799:                        continue;
2800:
2801:                    if (chA == chB)
2802:                        continue;
2803:                    else if (chA < chB)
2804:                        return -1;
2805:                    else
2806:                        return 1;
2807:                }
2808:
2809:                if (aLen == bLen)
2810:                    return 0;
2811:                else if (aLen < bLen)
2812:                    return -1;
2813:                else
2814:                    return 1;
2815:            }
2816:
2817:            /**
2818:             * Finds the index of a substring
2819:             *
2820:             * @param env the calling environment
2821:             */
2822:            public static Value strchr(Env env, StringValue haystack,
2823:                    Value needle) {
2824:                return strstr(env, haystack, needle);
2825:            }
2826:
2827:            /**
2828:             * Locale-based comparison
2829:             * XXX: i18n
2830:             *
2831:             * @param a left value
2832:             * @param b right value
2833:             * @return -1, 0, or 1
2834:             */
2835:            public static Value strcoll(String a, String b) {
2836:                if (a == null)
2837:                    a = "";
2838:
2839:                if (b == null)
2840:                    b = "";
2841:
2842:                int cmp = a.compareTo(b);
2843:
2844:                if (cmp == 0)
2845:                    return LongValue.ZERO;
2846:                else if (cmp < 0)
2847:                    return LongValue.MINUS_ONE;
2848:                else
2849:                    return LongValue.ONE;
2850:            }
2851:
2852:            /**
2853:             * Finds the number of initial characters in <i>string</i> that do not match
2854:             * one of the characters in <i>characters</i>
2855:             *
2856:             * @param string the string to search in
2857:             * @param characters the character set
2858:             * @param offset the starting offset
2859:             * @param length the length
2860:             *
2861:             * @return the length of the match or FALSE if the offset or length are invalid
2862:             */
2863:            public static Value strcspn(StringValue string,
2864:                    StringValue characters, @Optional("0")
2865:                    int offset, @Optional("-2147483648")
2866:                    int length) {
2867:                return strspnImpl(string, characters, offset, length, false);
2868:            }
2869:
2870:            /**
2871:             * Removes tags from a string.
2872:             *
2873:             * @param string the string to remove
2874:             * @param allowTags the allowable tags
2875:             */
2876:            public static StringValue strip_tags(StringValue string, @Optional
2877:            String allowTags) {
2878:                // XXX: allowTags is stubbed
2879:
2880:                StringValue result = string
2881:                        .createStringBuilder(string.length());
2882:
2883:                int len = string.length();
2884:
2885:                for (int i = 0; i < len; i++) {
2886:                    char ch = string.charAt(i);
2887:
2888:                    if (ch != '<') {
2889:                        result.append(ch);
2890:                        continue;
2891:                    }
2892:
2893:                    for (i++; i < len; i++) {
2894:                        ch = string.charAt(i);
2895:
2896:                        if (ch == '>')
2897:                            break;
2898:                    }
2899:                }
2900:
2901:                return result;
2902:            }
2903:
2904:            /**
2905:             * Returns the length of a string.
2906:             *
2907:             * @param value the argument value
2908:             */
2909:            public static Value strlen(Value value) {
2910:                return LongValue.create(value.length());
2911:            }
2912:
2913:            /**
2914:             * Case-insensitive comparison
2915:             *
2916:             * @param a left value
2917:             * @param b right value
2918:             * @return -1, 0, or 1
2919:             */
2920:            public static int strnatcasecmp(StringValue a, StringValue b) {
2921:                return naturalOrderCompare(a, b, true);
2922:            }
2923:
2924:            /**
2925:             * Case-sensitive comparison
2926:             *
2927:             * @param a left value
2928:             * @param b right value
2929:             * @return -1, 0, or 1
2930:             */
2931:            public static int strnatcmp(StringValue a, StringValue b) {
2932:                return naturalOrderCompare(a, b, false);
2933:            }
2934:
2935:            /**
2936:             * http://sourcefrog.net/projects/natsort/
2937:             */
2938:            private static int naturalOrderCompare(StringValue a,
2939:                    StringValue b, boolean ignoreCase) {
2940:                SimpleStringReader aIn = new SimpleStringReader(a);
2941:                SimpleStringReader bIn = new SimpleStringReader(b);
2942:
2943:                int aChar = aIn.read();
2944:                int bChar = bIn.read();
2945:
2946:                if (aChar == -1 && bChar >= 0)
2947:                    return -1;
2948:                else if (aChar >= 0 && bChar == -1)
2949:                    return 1;
2950:
2951:                while (true) {
2952:                    while (Character.isWhitespace(aChar)) {
2953:                        aChar = aIn.read();
2954:                    }
2955:
2956:                    while (Character.isWhitespace(bChar)) {
2957:                        bChar = bIn.read();
2958:                    }
2959:
2960:                    if (aChar == -1 && bChar == -1) {
2961:                        return 0;
2962:                    }
2963:
2964:                    // leading zeros
2965:                    // '01' < '2'
2966:                    // '0a' > 'a'
2967:                    if (aChar == '0' && bChar == '0') {
2968:                        while (true) {
2969:                            aChar = aIn.read();
2970:                            bChar = bIn.read();
2971:
2972:                            if (aChar == '0' && bChar == '0') {
2973:                                continue;
2974:                            } else if (aChar == '0') {
2975:                                if ('1' <= bChar && bChar <= '9')
2976:                                    return -1;
2977:                                else
2978:                                    return 1;
2979:                            } else if (bChar == 0) {
2980:                                if ('1' <= aChar && aChar <= '9')
2981:                                    return 1;
2982:                                else
2983:                                    return -1;
2984:                            } else {
2985:                                break;
2986:                            }
2987:                        }
2988:                    } else if ('0' < aChar && aChar <= '9' && '0' < bChar
2989:                            && bChar <= '9') {
2990:                        int aInteger = aIn.readInt(aChar);
2991:                        int bInteger = bIn.readInt(bChar);
2992:
2993:                        if (aInteger > bInteger)
2994:                            return 1;
2995:                        else if (aInteger < bInteger)
2996:                            return -1;
2997:                        else {
2998:                            aChar = aIn.read();
2999:                            bChar = bIn.read();
3000:                        }
3001:                    }
3002:
3003:                    if (ignoreCase) {
3004:                        aChar = Character.toUpperCase(aChar);
3005:                        bChar = Character.toUpperCase(bChar);
3006:                    }
3007:
3008:                    if (aChar > bChar)
3009:                        return 1;
3010:                    else if (aChar < bChar)
3011:                        return -1;
3012:
3013:                    aChar = aIn.read();
3014:                    bChar = bIn.read();
3015:
3016:                    // trailing spaces
3017:                    // "abc " > "abc"
3018:                    if (aChar >= 0 && bChar == -1)
3019:                        return 1;
3020:                    else if (aChar == -1 && bChar >= 0)
3021:                        return -1;
3022:                }
3023:            }
3024:
3025:            /**
3026:             * Case-insensitive comparison
3027:             *
3028:             * @param a left value
3029:             * @param b right value
3030:             * @return -1, 0, or 1
3031:             */
3032:            public static int strncasecmp(StringValue a, StringValue b,
3033:                    int length) {
3034:                int aLen = a.length();
3035:                int bLen = b.length();
3036:
3037:                for (int i = 0; i < length; i++) {
3038:                    if (aLen <= i)
3039:                        return -1;
3040:                    else if (bLen <= i)
3041:                        return 1;
3042:
3043:                    char aChar = Character.toUpperCase(a.charAt(i));
3044:                    char bChar = Character.toUpperCase(b.charAt(i));
3045:
3046:                    if (aChar < bChar)
3047:                        return -1;
3048:                    else if (bChar < aChar)
3049:                        return 1;
3050:                }
3051:
3052:                return 0;
3053:            }
3054:
3055:            /**
3056:             * Case-sensitive comparison
3057:             *
3058:             * @param a left value
3059:             * @param b right value
3060:             * @return -1, 0, or 1
3061:             */
3062:            public static int strncmp(StringValue a, StringValue b, int length) {
3063:                if (length < a.length())
3064:                    a = a.substring(0, length);
3065:
3066:                if (length < b.length())
3067:                    b = b.substring(0, length);
3068:
3069:                return strcmp(a, b);
3070:            }
3071:
3072:            /**
3073:             * Returns a substring of <i>haystack</i> starting from the earliest
3074:             * occurence of any char in <i>charList</i>
3075:             * 
3076:             * @param haystack the string to search in
3077:             * @param charList list of chars that would trigger match
3078:             * @return substring, else FALSE
3079:             */
3080:            public static Value strpbrk(StringValue haystack,
3081:                    StringValue charList) {
3082:                int len = haystack.length();
3083:                int sublen = charList.length();
3084:
3085:                for (int i = 0; i < len; i++) {
3086:                    for (int j = 0; j < sublen; j++) {
3087:                        if (haystack.charAt(i) == charList.charAt(j))
3088:                            return haystack.substring(i);
3089:                    }
3090:                }
3091:
3092:                return BooleanValue.FALSE;
3093:            }
3094:
3095:            /**
3096:             * Returns the position of a substring.
3097:             *
3098:             * @param haystack the string to search in
3099:             * @param needleV the string to search for
3100:             */
3101:            public static Value strpos(StringValue haystack, Value needleV,
3102:                    @Optional
3103:                    int offset) {
3104:                StringValue needle;
3105:
3106:                if (needleV instanceof  StringValue)
3107:                    needle = (StringValue) needleV;
3108:                else
3109:                    needle = StringValue.create((char) needleV.toInt());
3110:
3111:                int pos = haystack.indexOf(needle, offset);
3112:
3113:                if (pos < 0)
3114:                    return BooleanValue.FALSE;
3115:                else
3116:                    return LongValue.create(pos);
3117:            }
3118:
3119:            /**
3120:             * Returns the position of a substring, testing case insensitive.
3121:             *
3122:             * @param haystack the full argument to check
3123:             * @param needleV the substring argument to check
3124:             * @param offsetV optional starting position
3125:             */
3126:            public static Value stripos(StringValue haystack, Value needleV,
3127:                    @Optional
3128:                    int offset) {
3129:                StringValue needle;
3130:
3131:                if (needleV instanceof  StringValue)
3132:                    needle = (StringValue) needleV;
3133:                else
3134:                    needle = StringValue.create((char) needleV.toInt());
3135:
3136:                haystack = haystack.toLowerCase();
3137:                needle = needle.toLowerCase();
3138:
3139:                int pos = haystack.indexOf(needle, offset);
3140:
3141:                if (pos < 0)
3142:                    return BooleanValue.FALSE;
3143:                else
3144:                    return LongValue.create(pos);
3145:            }
3146:
3147:            /**
3148:             * Strip out the backslashes, recognizing the escape sequences, octal,
3149:             * and hexadecimal representations.
3150:             *
3151:             * @param source the string to clean
3152:             * @see #addcslashes
3153:             */
3154:            public static String stripcslashes(String source) {
3155:                if (source == null)
3156:                    source = "";
3157:
3158:                StringBuilder result = new StringBuilder(source.length());
3159:
3160:                int length = source.length();
3161:
3162:                for (int i = 0; i < length; i++) {
3163:                    int ch = source.charAt(i);
3164:
3165:                    if (ch == '\\') {
3166:                        i++;
3167:
3168:                        if (i == length)
3169:                            ch = '\\';
3170:                        else {
3171:                            ch = source.charAt(i);
3172:
3173:                            switch (ch) {
3174:                            case 'a':
3175:                                ch = 0x07;
3176:                                break;
3177:                            case 'b':
3178:                                ch = '\b';
3179:                                break;
3180:                            case 't':
3181:                                ch = '\t';
3182:                                break;
3183:                            case 'n':
3184:                                ch = '\n';
3185:                                break;
3186:                            case 'v':
3187:                                ch = 0xb;
3188:                                break;
3189:                            case 'f':
3190:                                ch = '\f';
3191:                                break;
3192:                            case 'r':
3193:                                ch = '\r';
3194:                                break;
3195:                            case 'x':
3196:                                // up to two digits for a hex number
3197:                                if (i + 1 == length)
3198:                                    break;
3199:
3200:                                int digitValue = hexToDigit(source
3201:                                        .charAt(i + 1));
3202:
3203:                                if (digitValue < 0)
3204:                                    break;
3205:
3206:                                ch = digitValue;
3207:                                i++;
3208:
3209:                                if (i + 1 == length)
3210:                                    break;
3211:
3212:                                digitValue = hexToDigit(source.charAt(i + 1));
3213:
3214:                                if (digitValue < 0)
3215:                                    break;
3216:
3217:                                ch = ((ch << 4) | digitValue);
3218:                                i++;
3219:
3220:                                break;
3221:                            default:
3222:                                // up to three digits from 0 to 7 for an octal number
3223:                                digitValue = octToDigit((char) ch);
3224:
3225:                                if (digitValue < 0)
3226:                                    break;
3227:
3228:                                ch = digitValue;
3229:
3230:                                if (i + 1 == length)
3231:                                    break;
3232:
3233:                                digitValue = octToDigit(source.charAt(i + 1));
3234:
3235:                                if (digitValue < 0)
3236:                                    break;
3237:
3238:                                ch = ((ch << 3) | digitValue);
3239:                                i++;
3240:
3241:                                if (i + 1 == length)
3242:                                    break;
3243:
3244:                                digitValue = octToDigit(source.charAt(i + 1));
3245:
3246:                                if (digitValue < 0)
3247:                                    break;
3248:
3249:                                ch = ((ch << 3) | digitValue);
3250:                                i++;
3251:                            }
3252:                        }
3253:                    } // if ch == '/'
3254:
3255:                    result.append((char) ch);
3256:                }
3257:
3258:                return result.toString();
3259:            }
3260:
3261:            /**
3262:             * Strips out the backslashes.
3263:             *
3264:             * @param string the string to clean
3265:             */
3266:            public static StringValue stripslashes(StringValue string) {
3267:                StringValue sb = string.createStringBuilder();
3268:                int len = string.length();
3269:
3270:                for (int i = 0; i < len; i++) {
3271:                    char ch = string.charAt(i);
3272:
3273:                    if (ch == '\\') {
3274:                        if (i + 1 < len) {
3275:                            sb.append(string.charAt(i + 1));
3276:                            i++;
3277:                        }
3278:                    } else
3279:                        sb.append(ch);
3280:                }
3281:
3282:                return sb;
3283:            }
3284:
3285:            /**
3286:             * Finds the first instance of a substring, testing case insensitively
3287:             *
3288:             * @param haystack the string to search in
3289:             * @param needleV the string to search for
3290:             * @return the trailing match or FALSE
3291:             */
3292:            public static Value stristr(StringValue haystack, Value needleV) {
3293:                CharSequence needleLower;
3294:
3295:                if (needleV instanceof  StringValue) {
3296:                    needleLower = ((StringValue) needleV).toLowerCase();
3297:                } else {
3298:                    char lower = Character.toLowerCase((char) needleV.toLong());
3299:
3300:                    needleLower = String.valueOf(lower);
3301:                }
3302:
3303:                StringValue haystackLower = haystack.toLowerCase();
3304:
3305:                int i = haystackLower.indexOf(needleLower);
3306:
3307:                if (i >= 0)
3308:                    return haystack.substring(i);
3309:                else
3310:                    return BooleanValue.FALSE;
3311:            }
3312:
3313:            /**
3314:             * Finds the last instance of a substring
3315:             *
3316:             * @param haystack the string to search in
3317:             * @param needleV the string to search for
3318:             * @return the trailing match or FALSE
3319:             */
3320:            public static Value strrchr(StringValue haystack, Value needleV) {
3321:                CharSequence needle;
3322:
3323:                if (needleV instanceof  StringValue)
3324:                    needle = (StringValue) needleV;
3325:                else
3326:                    needle = String.valueOf((char) needleV.toLong());
3327:
3328:                int i = haystack.lastIndexOf(needle);
3329:
3330:                if (i > 0)
3331:                    return haystack.substring(i);
3332:                else
3333:                    return BooleanValue.FALSE;
3334:            }
3335:
3336:            /**
3337:             * Reverses a string.
3338:             *
3339:             */
3340:            public static Value strrev(StringValue string) {
3341:                StringValue sb = string.createStringBuilder(string.length());
3342:
3343:                for (int i = string.length() - 1; i >= 0; i--) {
3344:                    sb.append(string.charAt(i));
3345:                }
3346:
3347:                return sb;
3348:            }
3349:
3350:            /**
3351:             * Returns the position of a substring.
3352:             *
3353:             * @param haystack the string to search in
3354:             * @param needleV the string to search for
3355:             */
3356:            public static Value strrpos(StringValue haystack, Value needleV,
3357:                    @Optional
3358:                    Value offsetV) {
3359:                StringValue needle;
3360:
3361:                if (needleV instanceof  StringValue)
3362:                    needle = needleV.toStringValue();
3363:                else
3364:                    needle = StringValue.create((char) needleV.toInt());
3365:
3366:                int offset;
3367:
3368:                if (offsetV instanceof  DefaultValue)
3369:                    offset = haystack.length();
3370:                else
3371:                    offset = offsetV.toInt();
3372:
3373:                int pos = haystack.lastIndexOf(needle, offset);
3374:
3375:                if (pos < 0)
3376:                    return BooleanValue.FALSE;
3377:                else
3378:                    return new LongValue(pos);
3379:            }
3380:
3381:            /**
3382:             * Returns the position of a substring, testing case-insensitive.
3383:             *
3384:             * @param haystack the full string to test
3385:             * @param needleV the substring string to test
3386:             * @param offsetV the optional offset to start searching
3387:             */
3388:            public static Value strripos(String haystack, Value needleV,
3389:                    @Optional
3390:                    Value offsetV) {
3391:                if (haystack == null)
3392:                    haystack = "";
3393:
3394:                String needle;
3395:
3396:                if (needleV instanceof  StringValue)
3397:                    needle = needleV.toString();
3398:                else
3399:                    needle = String.valueOf((char) needleV.toInt());
3400:
3401:                int offset;
3402:
3403:                if (offsetV instanceof  DefaultValue)
3404:                    offset = haystack.length();
3405:                else
3406:                    offset = offsetV.toInt();
3407:
3408:                haystack = haystack.toLowerCase();
3409:                needle = needle.toLowerCase();
3410:
3411:                int pos = haystack.lastIndexOf(needle, offset);
3412:
3413:                if (pos < 0)
3414:                    return BooleanValue.FALSE;
3415:                else
3416:                    return new LongValue(pos);
3417:            }
3418:
3419:            /**
3420:             * Finds the number of initial characters in <i>string</i> that match one of
3421:             * the characters in <i>characters</i>
3422:             *
3423:             * @param string the string to search in
3424:             * @param characters the character set
3425:             * @param offset the starting offset
3426:             * @param length the length
3427:             *
3428:             * @return the length of the match or FALSE if the offset or length are invalid
3429:             */
3430:            public static Value strspn(StringValue string,
3431:                    StringValue characters, @Optional
3432:                    int offset, @Optional("-2147483648")
3433:                    int length) {
3434:                return strspnImpl(string, characters, offset, length, true);
3435:            }
3436:
3437:            private static Value strspnImpl(StringValue string,
3438:                    StringValue characters, int offset, int length,
3439:                    boolean isMatch) {
3440:                int strlen = string.length();
3441:
3442:                // see also strcspn which uses the same procedure for determining
3443:                // effective offset and length
3444:                if (offset < 0) {
3445:                    offset += strlen;
3446:
3447:                    if (offset < 0)
3448:                        offset = 0;
3449:                }
3450:
3451:                if (offset > strlen)
3452:                    return BooleanValue.FALSE;
3453:
3454:                if (length == -2147483648)
3455:                    length = strlen;
3456:                else if (length < 0) {
3457:                    length += (strlen - offset);
3458:
3459:                    if (length < 0)
3460:                        length = 0;
3461:                }
3462:
3463:                int end = offset + length;
3464:
3465:                if (strlen < end)
3466:                    end = strlen;
3467:
3468:                int count = 0;
3469:
3470:                for (; offset < end; offset++) {
3471:                    char ch = string.charAt(offset);
3472:
3473:                    boolean isPresent = characters.indexOf(ch) > -1;
3474:
3475:                    if (isPresent == isMatch)
3476:                        count++;
3477:                    else
3478:                        return LongValue.create(count);
3479:                }
3480:
3481:                return LongValue.create(count);
3482:            }
3483:
3484:            /**
3485:             * Finds the first instance of a needle in haystack and returns
3486:             * the portion of haystack from the beginning of needle to the end of haystack.
3487:             *
3488:             * @param env the calling environment
3489:             * @param haystack the string to search in
3490:             * @param needleV the string to search for, or the oridinal value of a character
3491:             * @return the trailing match or FALSE if needle is not found
3492:             */
3493:            public static Value strstr(Env env, StringValue haystackV,
3494:                    Value needleV) {
3495:                if (haystackV == null)
3496:                    haystackV = env.createEmptyString();
3497:
3498:                String needle;
3499:
3500:                if (needleV instanceof  StringValue) {
3501:                    needle = needleV.toString();
3502:                } else {
3503:                    needle = String.valueOf((char) needleV.toLong());
3504:                }
3505:
3506:                if (needle.length() == 0) {
3507:                    env.warning("empty needle");
3508:                    return BooleanValue.FALSE;
3509:                }
3510:
3511:                int i = haystackV.indexOf(needle);
3512:
3513:                if (i >= 0)
3514:                    return haystackV.substring(i);
3515:                else
3516:                    return BooleanValue.FALSE;
3517:            }
3518:
3519:            /**
3520:             * Split a string into tokens using any character in another string as a delimiter.
3521:             *
3522:             * The first call establishes the string to search and the characters to use as tokens,
3523:             * the first token is returned:
3524:             * <pre>
3525:             *   strtok("hello, world", ", ")
3526:             *     => "hello"
3527:             * </pre>
3528:             *
3529:             * Subsequent calls pass only the token characters, the next token is returned:
3530:             * <pre>
3531:             *   strtok("hello, world", ", ")
3532:             *     => "hello"
3533:             *   strtok(", ")
3534:             *     => "world"
3535:             * </pre>
3536:             *
3537:             * False is returned if there are no more tokens:
3538:             * <pre>
3539:             *   strtok("hello, world", ", ")
3540:             *     => "hello"
3541:             *   strtok(", ")
3542:             *     => "world"
3543:             *   strtok(", ")
3544:             *     => false
3545:             * </pre>
3546:             *
3547:             * Calls that pass two arguments reset the search string:
3548:             * <pre>
3549:             *   strtok("hello, world", ", ")
3550:             *     => "hello"
3551:             *   strtok("goodbye, world", ", ")
3552:             *     => "goodbye"
3553:             *   strtok("world")
3554:             *     => false
3555:             *   strtok(", ")
3556:             *     => false
3557:             * </pre>
3558:             */
3559:            public static Value strtok(Env env, StringValue string1, @Optional
3560:            Value string2) {
3561:                StringValue string;
3562:                StringValue characters;
3563:                int offset;
3564:
3565:                if (string2.isNull()) {
3566:                    StringValue savedString = (StringValue) env
3567:                            .getSpecialValue("caucho.strtok_string");
3568:                    Integer savedOffset = (Integer) env
3569:                            .getSpecialValue("caucho.strtok_offset");
3570:
3571:                    string = savedString == null ? env.createEmptyString()
3572:                            : savedString;
3573:                    offset = savedOffset == null ? 0 : savedOffset;
3574:                    characters = string1;
3575:                } else {
3576:                    string = string1;
3577:                    offset = 0;
3578:                    characters = string2.toStringValue();
3579:
3580:                    env.setSpecialValue("caucho.strtok_string", string);
3581:                }
3582:
3583:                int strlen = string.length();
3584:
3585:                // skip any at beginning
3586:                for (; offset < strlen; offset++) {
3587:                    char ch = string.charAt(offset);
3588:
3589:                    if (characters.indexOf(ch) < 0)
3590:                        break;
3591:                }
3592:
3593:                Value result;
3594:
3595:                if (offset == strlen)
3596:                    result = BooleanValue.FALSE;
3597:                else {
3598:                    int start = offset;
3599:
3600:                    offset++;
3601:
3602:                    // find end
3603:                    for (; offset < strlen; offset++) {
3604:                        char ch = string.charAt(offset);
3605:
3606:                        if (characters.indexOf(ch) > -1)
3607:                            break;
3608:                    }
3609:
3610:                    result = string.substring(start, offset);
3611:                }
3612:
3613:                env.setSpecialValue("caucho.strtok_offset", offset);
3614:
3615:                return result;
3616:            }
3617:
3618:            /**
3619:             * Converts to lower case.
3620:             *
3621:             * @param string the input string
3622:             */
3623:            public static StringValue strtolower(StringValue string) {
3624:                return string.toLowerCase();
3625:            }
3626:
3627:            /**
3628:             * Converts to upper case.
3629:             *
3630:             * @param string the input string
3631:             */
3632:            public static StringValue strtoupper(StringValue string) {
3633:                return string.toUpperCase();
3634:            }
3635:
3636:            /**
3637:             * Translates characters in a string to target values.
3638:             *
3639:             * @param string the source string
3640:             * @param fromV the from characters
3641:             * @param to the to character map
3642:             */
3643:            public static StringValue strtr(Env env, StringValue string,
3644:                    Value fromV, @Optional
3645:                    StringValue to) {
3646:                if (fromV instanceof  ArrayValue)
3647:                    return strtrArray(string, (ArrayValue) fromV);
3648:
3649:                StringValue from = fromV.toStringValue();
3650:
3651:                int len = from.length();
3652:
3653:                if (to.length() < len)
3654:                    len = to.length();
3655:
3656:                char[] map = new char[256];
3657:                for (int i = len - 1; i >= 0; i--)
3658:                    map[from.charAt(i)] = to.charAt(i);
3659:
3660:                StringValue sb = string.createStringBuilder();
3661:
3662:                len = string.length();
3663:                for (int i = 0; i < len; i++) {
3664:                    char ch = string.charAt(i);
3665:
3666:                    if (map[ch] != 0)
3667:                        sb.append(map[ch]);
3668:                    else
3669:                        sb.append(ch);
3670:                }
3671:
3672:                return sb;
3673:            }
3674:
3675:            /**
3676:             * Translates characters in a string to target values.
3677:             *
3678:             * @param string the source string
3679:             * @param map the character map
3680:             */
3681:            private static StringValue strtrArray(StringValue string,
3682:                    ArrayValue map) {
3683:                int size = map.getSize();
3684:
3685:                StringValue[] fromList = new StringValue[size];
3686:                StringValue[] toList = new StringValue[size];
3687:
3688:                Map.Entry<Value, Value>[] entryArray = new Map.Entry[size];
3689:
3690:                int i = 0;
3691:                for (Map.Entry<Value, Value> entry : map.entrySet()) {
3692:                    entryArray[i++] = entry;
3693:                }
3694:
3695:                // sort entries in descending fashion
3696:                Arrays.sort(entryArray,
3697:                        new StrtrComparator<Map.Entry<Value, Value>>());
3698:
3699:                boolean[] charSet = new boolean[256];
3700:
3701:                for (i = 0; i < size; i++) {
3702:                    fromList[i] = entryArray[i].getKey().toStringValue();
3703:                    toList[i] = entryArray[i].getValue().toStringValue();
3704:
3705:                    charSet[fromList[i].charAt(0)] = true;
3706:                }
3707:
3708:                StringValue result = string.createStringBuilder();
3709:                int len = string.length();
3710:                int head = 0;
3711:
3712:                top: while (head < len) {
3713:                    char ch = string.charAt(head);
3714:
3715:                    if (charSet.length <= ch || charSet[ch]) {
3716:                        fromLoop: for (i = 0; i < fromList.length; i++) {
3717:                            StringValue from = fromList[i];
3718:                            int fromLen = from.length();
3719:
3720:                            if (head + fromLen > len)
3721:                                continue;
3722:
3723:                            if (ch != from.charAt(0))
3724:                                continue;
3725:
3726:                            for (int j = 0; j < fromLen; j++) {
3727:                                if (string.charAt(head + j) != from.charAt(j))
3728:                                    continue fromLoop;
3729:                            }
3730:
3731:                            result = result.append(toList[i]);
3732:                            head = head + fromLen;
3733:
3734:                            continue top;
3735:                        }
3736:                    }
3737:
3738:                    result.append(ch);
3739:                    head++;
3740:                }
3741:
3742:                return result;
3743:            }
3744:
3745:            /*
3746:             * Comparator for sorting in descending fashion based on length.
3747:             */
3748:            static class StrtrComparator<T extends Map.Entry<Value, Value>>
3749:                    implements  Comparator<T> {
3750:                public int compare(T a, T b) {
3751:                    int lenA = a.getKey().length();
3752:                    int lenB = b.getKey().length();
3753:
3754:                    if (lenA < lenB)
3755:                        return 1;
3756:                    else if (lenA == lenB)
3757:                        return 0;
3758:                    else
3759:                        return -1;
3760:                }
3761:            }
3762:
3763:            /**
3764:             * Returns a substring
3765:             *
3766:             * @param env the calling environment
3767:             * @param string the string
3768:             * @param start the start offset
3769:             * @param lenV the optional length
3770:             */
3771:            public static Value substr(Env env, StringValue string, int start,
3772:                    @Optional
3773:                    Value lenV) {
3774:                int strLen = string.length();
3775:                if (start < 0)
3776:                    start = strLen + start;
3777:
3778:                if (start < 0 || strLen < start)
3779:                    return BooleanValue.FALSE;
3780:
3781:                if (lenV instanceof  DefaultValue) {
3782:                    return string.substring(start);
3783:                } else {
3784:                    int len = lenV.toInt();
3785:                    int end;
3786:
3787:                    if (len < 0)
3788:                        end = strLen + len;
3789:                    else
3790:                        end = start + len;
3791:
3792:                    if (end <= start)
3793:                        return string.getEmptyString();
3794:                    else if (strLen <= end)
3795:                        return string.substring(start);
3796:                    else
3797:                        return string.substring(start, end);
3798:                }
3799:            }
3800:
3801:            public static Value substr_compare(Env env, StringValue mainStr,
3802:                    StringValue str, int offset, @Optional
3803:                    Value lenV, @Optional
3804:                    boolean isCaseInsensitive) {
3805:                int strLen = mainStr.length();
3806:
3807:                if (lenV.toInt() > strLen || offset > strLen
3808:                        || lenV.toInt() + offset > strLen) {
3809:                    return BooleanValue.FALSE;
3810:                }
3811:
3812:                mainStr = substr(env, mainStr, offset, lenV).toStringValue();
3813:
3814:                if (isCaseInsensitive)
3815:                    return LongValue.create(strcasecmp(mainStr, str));
3816:                else
3817:                    return LongValue.create(strcmp(mainStr, str));
3818:            }
3819:
3820:            public static Value substr_count(Env env, StringValue haystackV,
3821:                    StringValue needleV, @Optional("0")
3822:                    int offset, @Optional("-1")
3823:                    int length) {
3824:                String haystack = haystackV.toString();
3825:
3826:                String needle = needleV.toString();
3827:
3828:                if (needle.length() == 0) {
3829:                    env.warning(L.l("empty substr"));
3830:                    return BooleanValue.FALSE;
3831:                }
3832:
3833:                int haystackLength = haystack.length();
3834:
3835:                if (offset < 0 || offset > haystackLength) {
3836:                    env.warning(L.l("offset `{0}' out of range", offset));
3837:                    return BooleanValue.FALSE;
3838:                }
3839:
3840:                if (length > -1) {
3841:                    if (offset + length > haystackLength) {
3842:                        env.warning(L.l("length `{0}' out of range", length));
3843:                        return BooleanValue.FALSE;
3844:                    } else
3845:                        haystackLength = offset + length;
3846:                }
3847:
3848:                int needleLength = needle.length();
3849:
3850:                int count = 0;
3851:
3852:                int end = haystackLength - needleLength + 1;
3853:
3854:                for (int i = offset; i < end; i++) {
3855:                    if (haystack.startsWith(needle, i)) {
3856:                        count++;
3857:                        i += needleLength;
3858:                    }
3859:                }
3860:
3861:                return new LongValue(count);
3862:            }
3863:
3864:            /**
3865:             * Replaces a substring with a replacement
3866:             *
3867:             * @param subjectV a string to modify, or an array of strings to modify
3868:             * @param replacement the replacement string
3869:             * @param startV the start offset
3870:             * @param lengthV the optional length
3871:             */
3872:            public static Value substr_replace(Value subjectV,
3873:                    StringValue replacement, Value startV, @Optional
3874:                    Value lengthV) {
3875:                int start = 0;
3876:                int length = Integer.MAX_VALUE / 2;
3877:
3878:                if (!(lengthV.isNull() || lengthV.isArray()))
3879:                    length = lengthV.toInt();
3880:
3881:                if (!(startV.isNull() || startV.isArray()))
3882:                    start = startV.toInt();
3883:
3884:                Iterator<Value> startIterator = startV.isArray() ? ((ArrayValue) startV)
3885:                        .values().iterator()
3886:                        : null;
3887:
3888:                Iterator<Value> lengthIterator = lengthV.isArray() ? ((ArrayValue) lengthV)
3889:                        .values().iterator()
3890:                        : null;
3891:
3892:                if (subjectV.isArray()) {
3893:                    ArrayValue resultArray = new ArrayValueImpl();
3894:
3895:                    ArrayValue subjectArray = (ArrayValue) subjectV;
3896:
3897:                    for (Value value : subjectArray.values()) {
3898:
3899:                        if (lengthIterator != null && lengthIterator.hasNext())
3900:                            length = lengthIterator.next().toInt();
3901:
3902:                        if (startIterator != null && startIterator.hasNext())
3903:                            start = startIterator.next().toInt();
3904:
3905:                        Value result = substrReplaceImpl(value.toStringValue(),
3906:                                replacement, start, length);
3907:
3908:                        resultArray.append(result);
3909:                    }
3910:
3911:                    return resultArray;
3912:                } else {
3913:                    if (lengthIterator != null && lengthIterator.hasNext())
3914:                        length = lengthIterator.next().toInt();
3915:
3916:                    if (startIterator != null && startIterator.hasNext())
3917:                        start = startIterator.next().toInt();
3918:
3919:                    return substrReplaceImpl(subjectV.toStringValue(),
3920:                            replacement, start, length);
3921:                }
3922:            }
3923:
3924:            private static Value substrReplaceImpl(StringValue string,
3925:                    StringValue replacement, int start, int len) {
3926:                int strLen = string.length();
3927:
3928:                if (start > strLen)
3929:                    start = strLen;
3930:                else if (start < 0)
3931:                    start = Math.max(strLen + start, 0);
3932:
3933:                int end;
3934:
3935:                if (len < 0)
3936:                    end = Math.max(strLen + len, start);
3937:                else
3938:                    end = Math.min(start + len, strLen);
3939:
3940:                StringValue result = string.createStringBuilder();
3941:
3942:                result = result.append(string.substring(0, start));
3943:                result = result.append(replacement);
3944:                result = result.append(string.substring(end));
3945:
3946:                return result;
3947:            }
3948:
3949:            /**
3950:             * Removes leading and trailing whitespace.
3951:             *
3952:             * @param string the string to be trimmed
3953:             * @param characters optional set of characters to trim
3954:             * @return the trimmed string
3955:             */
3956:            public static Value trim(Env env, StringValue string, @Optional
3957:            String characters) {
3958:                boolean[] trim;
3959:
3960:                if (characters == null || characters.equals(""))
3961:                    trim = TRIM_WHITESPACE;
3962:                else
3963:                    trim = parseCharsetBitmap(characters.toString());
3964:
3965:                int len = string.length();
3966:
3967:                int head = 0;
3968:                for (; head < len; head++) {
3969:                    char ch = string.charAt(head);
3970:
3971:                    if (ch >= 256 || !trim[ch]) {
3972:                        break;
3973:                    }
3974:                }
3975:
3976:                int tail = len - 1;
3977:                for (; tail >= 0; tail--) {
3978:                    char ch = string.charAt(tail);
3979:
3980:                    if (ch >= 256 || !trim[ch]) {
3981:                        break;
3982:                    }
3983:                }
3984:
3985:                if (tail < head)
3986:                    return env.createEmptyString();
3987:                else {
3988:                    return (StringValue) string.subSequence(head, tail + 1);
3989:                }
3990:            }
3991:
3992:            /**
3993:             * Uppercases the first character
3994:             *
3995:             * @param string the input string
3996:             */
3997:            public static String ucfirst(String string) {
3998:                if (string == null)
3999:                    string = "";
4000:
4001:                if (string.length() == 0)
4002:                    return string;
4003:
4004:                return Character.toUpperCase(string.charAt(0))
4005:                        + string.substring(1);
4006:            }
4007:
4008:            /**
4009:             * Uppercases the first character of each word
4010:             *
4011:             * @param string the input string
4012:             */
4013:            public static String ucwords(String string) {
4014:                if (string == null)
4015:                    string = "";
4016:
4017:                int strLen = string.length();
4018:
4019:                boolean isStart = true;
4020:                StringBuilder sb = new StringBuilder();
4021:
4022:                for (int i = 0; i < strLen; i++) {
4023:                    char ch = string.charAt(i);
4024:
4025:                    switch (ch) {
4026:                    case ' ':
4027:                    case '\t':
4028:                    case '\r':
4029:                    case '\n':
4030:                        isStart = true;
4031:                        sb.append(ch);
4032:                        break;
4033:                    default:
4034:                        if (isStart)
4035:                            sb.append(Character.toUpperCase(ch));
4036:                        else
4037:                            sb.append(ch);
4038:                        isStart = false;
4039:                        break;
4040:                    }
4041:                }
4042:
4043:                return sb.toString();
4044:            }
4045:
4046:            /**
4047:             * Formatted strings with array arguments
4048:             *
4049:             * @param format the format string
4050:             * @param array the arguments to apply to the format string
4051:             */
4052:            public static int vprintf(Env env, StringValue format, @NotNull
4053:            ArrayValue array) {
4054:                Value[] args;
4055:
4056:                if (array != null) {
4057:                    args = new Value[array.getSize()];
4058:                    int i = 0;
4059:                    for (Value value : array.values())
4060:                        args[i++] = value;
4061:                } else
4062:                    args = new Value[0];
4063:
4064:                return printf(env, format, args);
4065:            }
4066:
4067:            /**
4068:             * Formatted strings with array arguments
4069:             *
4070:             * @param format the format string
4071:             * @param array the arguments to apply to the format string
4072:             */
4073:            public static Value vsprintf(StringValue format, @NotNull
4074:            ArrayValue array) {
4075:                Value[] args;
4076:
4077:                if (array != null) {
4078:                    args = new Value[array.getSize()];
4079:                    int i = 0;
4080:                    for (Value value : array.values())
4081:                        args[i++] = value;
4082:                } else
4083:                    args = new Value[0];
4084:
4085:                return sprintf(format, args);
4086:            }
4087:
4088:            /**
4089:             * Wraps a string to the given number of characters.
4090:             *
4091:             * @param string the input string
4092:             * @param width the width
4093:             * @param breakString the break string
4094:             * @param cut if true, break on exact match
4095:             */
4096:            public static String wordwrap(String string, @Optional("75")
4097:            int width, @Optional("'\n'")
4098:            String breakString, @Optional
4099:            boolean cut) {
4100:                if (string == null)
4101:                    string = "";
4102:
4103:                if (breakString == null)
4104:                    breakString = "";
4105:
4106:                int len = string.length();
4107:                int head = 0;
4108:
4109:                StringBuilder sb = new StringBuilder();
4110:                while (head + width < len) {
4111:                    int newline = string.indexOf('\n', head + 1);
4112:
4113:                    int tail = head + width;
4114:
4115:                    if (newline > 0 && newline < tail) {
4116:                        if (sb.length() > 0)
4117:                            sb.append(breakString);
4118:
4119:                        sb.append(string.substring(head, newline));
4120:                        head = newline + 1;
4121:                        continue;
4122:                    }
4123:
4124:                    if (!cut) {
4125:                        for (; head < tail
4126:                                && !Character.isWhitespace(string.charAt(tail)); tail--) {
4127:                        }
4128:
4129:                        if (head == tail)
4130:                            tail = head + width;
4131:                    }
4132:
4133:                    if (sb.length() > 0)
4134:                        sb.append(breakString);
4135:
4136:                    sb.append(string.substring(head, tail));
4137:
4138:                    head = tail;
4139:
4140:                    if (!cut && head < len
4141:                            && Character.isWhitespace(string.charAt(head)))
4142:                        head++;
4143:                }
4144:
4145:                if (head < len) {
4146:                    if (sb.length() > 0)
4147:                        sb.append(breakString);
4148:
4149:                    sb.append(string.substring(head));
4150:                }
4151:
4152:                return sb.toString();
4153:            }
4154:
4155:            /**
4156:             * Returns true if the character is a whitespace character.
4157:             */
4158:            protected static boolean isWhitespace(char ch) {
4159:                return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
4160:            }
4161:
4162:            /**
4163:             * Returns the uppercase equivalent of the caharacter
4164:             */
4165:            protected static char toUpperCase(char ch) {
4166:                if (ch >= 'a' && ch <= 'z')
4167:                    return (char) ('A' + (ch - 'a'));
4168:                else
4169:                    return ch;
4170:            }
4171:
4172:            /**
4173:             * Converts an integer digit to a uuencoded char.
4174:             */
4175:            protected static char toUUChar(int d) {
4176:                if (d == 0)
4177:                    return (char) 0x60;
4178:                else
4179:                    return (char) (0x20 + (d & 0x3f));
4180:            }
4181:
4182:            protected static char toHexChar(int d) {
4183:                d &= 0xf;
4184:
4185:                if (d < 10)
4186:                    return (char) (d + '0');
4187:                else
4188:                    return (char) (d - 10 + 'a');
4189:            }
4190:
4191:            protected static char toUpperHexChar(int d) {
4192:                d &= 0xf;
4193:
4194:                if (d < 10)
4195:                    return (char) (d + '0');
4196:                else
4197:                    return (char) (d - 10 + 'A');
4198:            }
4199:
4200:            protected static int hexToDigit(char ch) {
4201:                if ('0' <= ch && ch <= '9')
4202:                    return ch - '0';
4203:                else if ('a' <= ch && ch <= 'f')
4204:                    return ch - 'a' + 10;
4205:                else if ('A' <= ch && ch <= 'F')
4206:                    return ch - 'A' + 10;
4207:                else
4208:                    return -1;
4209:            }
4210:
4211:            protected static int octToDigit(char ch) {
4212:                if ('0' <= ch && ch <= '7')
4213:                    return ch - '0';
4214:                else
4215:                    return -1;
4216:            }
4217:
4218:            abstract static class PrintfSegment {
4219:                abstract public void apply(StringValue sb, Value[] args);
4220:
4221:                static boolean hasIndex(String format) {
4222:                    return format.indexOf('$') >= 0;
4223:                }
4224:
4225:                static int getIndex(String format) {
4226:                    int value = 0;
4227:
4228:                    for (int i = 0; i < format.length(); i++) {
4229:                        char ch;
4230:
4231:                        if ('0' <= (ch = format.charAt(i)) && ch <= '9')
4232:                            value = 10 * value + ch - '0';
4233:                        else
4234:                            break;
4235:                    }
4236:
4237:                    return value - 1;
4238:                }
4239:
4240:                static String getIndexFormat(String format) {
4241:                    int p = format.indexOf('$');
4242:
4243:                    return '%' + format.substring(p + 1);
4244:                }
4245:            }
4246:
4247:            static class TextPrintfSegment extends PrintfSegment {
4248:                private final char[] _text;
4249:
4250:                TextPrintfSegment(StringBuilder text) {
4251:                    _text = new char[text.length()];
4252:
4253:                    text.getChars(0, _text.length, _text, 0);
4254:                }
4255:
4256:                public void apply(StringValue sb, Value[] args) {
4257:                    sb.append(_text, 0, _text.length);
4258:                }
4259:            }
4260:
4261:            static class LongPrintfSegment extends PrintfSegment {
4262:                private final String _format;
4263:                private final int _index;
4264:
4265:                private LongPrintfSegment(String format, int index) {
4266:                    _format = format;
4267:                    _index = index;
4268:                }
4269:
4270:                static PrintfSegment create(String format, int index) {
4271:                    if (hasIndex(format)) {
4272:                        index = getIndex(format);
4273:                        format = getIndexFormat(format);
4274:                    } else {
4275:                        format = '%' + format;
4276:                        index = index;
4277:                    }
4278:
4279:                    // php/115b
4280:                    // strip out illegal precision specifier from phpBB vote function
4281:                    if (format.length() > 1 && format.charAt(1) == '.') {
4282:                        int i;
4283:
4284:                        for (i = 2; i < format.length(); i++) {
4285:                            char ch = format.charAt(i);
4286:
4287:                            if (!('0' <= ch && ch <= '9'))
4288:                                break;
4289:                        }
4290:
4291:                        format = '%' + format.substring(i);
4292:                    }
4293:
4294:                    if (format.charAt(format.length() - 1) == 'x'
4295:                            || format.charAt(format.length() - 1) == 'X') {
4296:                        HexPrintfSegment hex = HexPrintfSegment.create(format,
4297:                                index);
4298:
4299:                        if (hex != null)
4300:                            return hex;
4301:                    }
4302:
4303:                    return new LongPrintfSegment(format, index);
4304:                }
4305:
4306:                public void apply(StringValue sb, Value[] args) {
4307:                    long value;
4308:
4309:                    if (_index < args.length)
4310:                        value = args[_index].toLong();
4311:                    else
4312:                        value = 0;
4313:
4314:                    sb.append(String.format(_format, value));
4315:                }
4316:            }
4317:
4318:            static class HexPrintfSegment extends PrintfSegment {
4319:                private final int _index;
4320:                private final int _min;
4321:                private final char _pad;
4322:                private boolean _isUpper;
4323:
4324:                HexPrintfSegment(int index, int min, char pad, boolean isUpper) {
4325:                    _index = index;
4326:                    _min = min;
4327:                    _pad = pad;
4328:                    _isUpper = isUpper;
4329:                }
4330:
4331:                static HexPrintfSegment create(String format, int index) {
4332:                    int length = format.length();
4333:                    int offset = 1;
4334:
4335:                    boolean isUpper = format.charAt(length - 1) == 'X';
4336:                    char pad = ' ';
4337:
4338:                    if (format.charAt(offset) == ' ') {
4339:                        pad = ' ';
4340:                        offset++;
4341:                    } else if (format.charAt(offset) == '0') {
4342:                        pad = '0';
4343:                        offset++;
4344:                    }
4345:
4346:                    int min = 0;
4347:                    for (; offset < length - 1; offset++) {
4348:                        char ch = format.charAt(offset);
4349:
4350:                        if ('0' <= ch && ch <= '9')
4351:                            min = 10 * min + ch - '0';
4352:                        else
4353:                            return null;
4354:                    }
4355:
4356:                    return new HexPrintfSegment(index, min, pad, isUpper);
4357:                }
4358:
4359:                public void apply(StringValue sb, Value[] args) {
4360:                    long value;
4361:
4362:                    if (_index >= 0 && _index < args.length)
4363:                        value = args[_index].toLong();
4364:                    else
4365:                        value = 0;
4366:
4367:                    int digits = 0;
4368:
4369:                    long shift = value;
4370:                    for (int i = 0; i < 16; i++) {
4371:                        if (shift != 0)
4372:                            digits = i;
4373:
4374:                        shift = shift >>> 4;
4375:                    }
4376:
4377:                    for (int i = digits + 1; i < _min; i++)
4378:                        sb.append(_pad);
4379:
4380:                    for (; digits >= 0; digits--) {
4381:                        int digit = (int) (value >>> (4 * digits)) & 0xf;
4382:
4383:                        if (digit <= 9)
4384:                            sb.append((char) ('0' + digit));
4385:                        else if (_isUpper)
4386:                            sb.append((char) ('A' + digit - 10));
4387:                        else
4388:                            sb.append((char) ('a' + digit - 10));
4389:                    }
4390:                }
4391:            }
4392:
4393:            static class DoublePrintfSegment extends PrintfSegment {
4394:                private final String _format;
4395:                private final int _index;
4396:
4397:                DoublePrintfSegment(String format, int index) {
4398:                    if (hasIndex(format)) {
4399:                        _index = getIndex(format);
4400:                        _format = getIndexFormat(format);
4401:                    } else {
4402:                        _format = '%' + format;
4403:                        _index = index;
4404:                    }
4405:                }
4406:
4407:                public void apply(StringValue sb, Value[] args) {
4408:                    double value;
4409:
4410:                    if (_index < args.length)
4411:                        value = args[_index].toDouble();
4412:                    else
4413:                        value = 0;
4414:
4415:                    sb.append(String.format(_format, value));
4416:                }
4417:            }
4418:
4419:            static class StringPrintfSegment extends PrintfSegment {
4420:                private final char[] _prefix;
4421:                private final int _min;
4422:                private final int _max;
4423:                private final boolean _isLeft;
4424:                private final boolean _isUpper;
4425:                private final char _pad;
4426:                protected final int _index;
4427:
4428:                StringPrintfSegment(StringBuilder prefix, boolean isLeft,
4429:                        boolean isZero, boolean isUpper, String format,
4430:                        int index) {
4431:                    _prefix = new char[prefix.length()];
4432:
4433:                    _isLeft = isLeft;
4434:                    _isUpper = isUpper;
4435:
4436:                    _pad = isZero ? '0' : ' ';
4437:
4438:                    prefix.getChars(0, _prefix.length, _prefix, 0);
4439:
4440:                    if (hasIndex(format)) {
4441:                        index = getIndex(format);
4442:                        format = getIndexFormat(format);
4443:                    }
4444:
4445:                    int i = 0;
4446:                    int len = format.length();
4447:
4448:                    int min = 0;
4449:                    int max = Integer.MAX_VALUE;
4450:                    char ch = ' ';
4451:
4452:                    for (; i < len && '0' <= (ch = format.charAt(i))
4453:                            && ch <= '9'; i++) {
4454:                        min = 10 * min + ch - '0';
4455:                    }
4456:
4457:                    if (ch == '.') {
4458:                        max = 0;
4459:
4460:                        for (i++; i < len && '0' <= (ch = format.charAt(i))
4461:                                && ch <= '9'; i++) {
4462:                            max = 10 * max + ch - '0';
4463:                        }
4464:                    }
4465:
4466:                    _min = min;
4467:                    _max = max;
4468:
4469:                    _index = index;
4470:                }
4471:
4472:                public void apply(StringValue sb, Value[] args) {
4473:                    sb.append(_prefix, 0, _prefix.length);
4474:
4475:                    String value = toValue(args);
4476:
4477:                    int len = value.length();
4478:
4479:                    if (_max < len) {
4480:                        value = value.substring(0, _max);
4481:                        len = _max;
4482:                    }
4483:
4484:                    if (_isUpper)
4485:                        value = value.toUpperCase();
4486:
4487:                    if (!_isLeft) {
4488:                        for (int i = len; i < _min; i++) {
4489:                            sb.append(_pad);
4490:                        }
4491:                    }
4492:
4493:                    sb.append(value);
4494:
4495:                    if (_isLeft) {
4496:                        for (int i = len; i < _min; i++) {
4497:                            sb.append(_pad);
4498:                        }
4499:                    }
4500:                }
4501:
4502:                String toValue(Value[] args) {
4503:                    if (_index < args.length)
4504:                        return args[_index].toString();
4505:                    else
4506:                        return "";
4507:                }
4508:            }
4509:
4510:            static class CharPrintfSegment extends StringPrintfSegment {
4511:                CharPrintfSegment(StringBuilder prefix, boolean isLeft,
4512:                        boolean isZero, boolean isUpper, String format,
4513:                        int index) {
4514:                    super (prefix, isLeft, isZero, isUpper, format, index);
4515:                }
4516:
4517:                String toValue(Value[] args) {
4518:                    if (args.length <= _index)
4519:                        return "";
4520:
4521:                    Value v = args[_index];
4522:
4523:                    if (v.isLongConvertible())
4524:                        return String.valueOf((char) v.toLong());
4525:                    else
4526:                        return v.charValueAt(0).toString();
4527:                }
4528:            }
4529:
4530:            static class SimpleStringReader {
4531:                StringValue _str;
4532:
4533:                int _length;
4534:                int _index;
4535:
4536:                SimpleStringReader(StringValue str) {
4537:                    _str = str;
4538:                    _length = str.length();
4539:                    _index = 0;
4540:                }
4541:
4542:                int read() {
4543:                    if (_index < _length)
4544:                        return _str.charAt(_index++);
4545:                    else
4546:                        return -1;
4547:                }
4548:
4549:                int peek() {
4550:                    if (_index < _length)
4551:                        return _str.charAt(_index);
4552:                    else
4553:                        return -1;
4554:
4555:                }
4556:
4557:                int readInt(int currChar) {
4558:                    int number = currChar - '0';
4559:
4560:                    while (true) {
4561:                        currChar = peek();
4562:
4563:                        if ('0' <= currChar && currChar <= '9') {
4564:                            number = number * 10 + currChar - '0';
4565:                            _index++;
4566:                        } else {
4567:                            break;
4568:                        }
4569:                    }
4570:
4571:                    return number;
4572:                }
4573:            }
4574:
4575:            static {
4576:                DEFAULT_DECIMAL_FORMAT_SYMBOLS = new DecimalFormatSymbols();
4577:                DEFAULT_DECIMAL_FORMAT_SYMBOLS.setDecimalSeparator('.');
4578:                DEFAULT_DECIMAL_FORMAT_SYMBOLS.setGroupingSeparator(',');
4579:                DEFAULT_DECIMAL_FORMAT_SYMBOLS.setZeroDigit('0');
4580:            }
4581:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.