Source Code Cross Referenced for Parser.java in  » Web-Framework » rife-1.6.1 » com » uwyn » rife » template » 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 » Web Framework » rife 1.6.1 » com.uwyn.rife.template 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
0003:         * Distributed under the terms of either:
0004:         * - the common development and distribution license (CDDL), v1.0; or
0005:         * - the GNU Lesser General Public License, v2.1 or later
0006:         * $Id: Parser.java 3782 2007-06-11 11:01:53Z gbevin $
0007:         */
0008:        package com.uwyn.rife.template;
0009:
0010:        import com.uwyn.rife.template.exceptions.*;
0011:        import java.util.*;
0012:
0013:        import com.uwyn.rife.config.RifeConfig;
0014:        import com.uwyn.rife.datastructures.DocumentPosition;
0015:        import com.uwyn.rife.pcj.list.IntArrayList;
0016:        import com.uwyn.rife.resources.ResourceFinder;
0017:        import com.uwyn.rife.resources.exceptions.ResourceFinderErrorException;
0018:        import com.uwyn.rife.tools.StringUtils;
0019:        import java.io.ByteArrayOutputStream;
0020:        import java.io.UnsupportedEncodingException;
0021:        import java.net.URL;
0022:        import java.util.regex.Matcher;
0023:        import java.util.regex.Pattern;
0024:
0025:        public class Parser implements  Cloneable {
0026:            public enum PartType {
0027:                CONTENT, TAG_START, TAG_END, TAG_TERM, TAG_SHORT_TERM, STRING_DELIMITER_BEGIN, STRING_DELIMITER_END, VALUE, BLOCK, BLOCKVALUE, BLOCKAPPEND, COMMENT, INCLUDE, UNESCAPE_START
0028:            }
0029:
0030:            public final static String DEFAULT_TEMPLATES_PATH = "templates/";
0031:
0032:            public final static String TEMPLATE_PACKAGE = "com.uwyn.rife.template.";
0033:
0034:            private TemplateFactory mTemplateFactory = null;
0035:
0036:            private String mIdentifier = null;
0037:            private String mPackageName = null;
0038:
0039:            private Config[] mConfigs = null;
0040:
0041:            private String mAmbiguousName = null;
0042:            private String mExtension = null;
0043:            private int mExtensionLength = -1;
0044:
0045:            private Pattern[] mBlockFilters = null;
0046:            private Pattern[] mValueFilters = null;
0047:
0048:            Parser(TemplateFactory templateFactory, String identifier,
0049:                    Config[] configs, String extension, Pattern[] blockFilters,
0050:                    Pattern[] valueFilters) {
0051:                init(templateFactory, identifier, configs, extension,
0052:                        blockFilters, valueFilters);
0053:            }
0054:
0055:            Config[] getConfigs() {
0056:                return mConfigs;
0057:            }
0058:
0059:            String getExtension() {
0060:                return mExtension;
0061:            }
0062:
0063:            Pattern[] getBlockFilters() {
0064:                return mBlockFilters;
0065:            }
0066:
0067:            Pattern[] getValueFilters() {
0068:                return mValueFilters;
0069:            }
0070:
0071:            TemplateFactory getTemplateFactory() {
0072:                return mTemplateFactory;
0073:            }
0074:
0075:            private void init(TemplateFactory templateFactory,
0076:                    String identifier, Config[] configs, String extension,
0077:                    Pattern[] blockFilters, Pattern[] valueFilters) {
0078:                assert templateFactory != null;
0079:                assert identifier != null;
0080:                assert configs != null;
0081:                assert configs.length > 0;
0082:                assert extension != null;
0083:
0084:                mTemplateFactory = templateFactory;
0085:
0086:                mIdentifier = identifier;
0087:                mPackageName = TEMPLATE_PACKAGE + mIdentifier + ".";
0088:
0089:                mConfigs = configs;
0090:
0091:                mExtension = extension;
0092:                mExtensionLength = mExtension.length();
0093:                mAmbiguousName = (mExtension + mExtension).substring(1);
0094:
0095:                mBlockFilters = blockFilters;
0096:                mValueFilters = valueFilters;
0097:
0098:                assert mExtensionLength > 0;
0099:            }
0100:
0101:            public Parser clone() {
0102:                Parser new_parser = null;
0103:                try {
0104:                    new_parser = (Parser) super .clone();
0105:                } catch (CloneNotSupportedException e) {
0106:                    new_parser = null;
0107:                }
0108:
0109:                Pattern[] new_blockfilters = null;
0110:                if (mBlockFilters != null) {
0111:                    new_blockfilters = mBlockFilters.clone();
0112:                }
0113:                Pattern[] new_valuefilters = null;
0114:                if (mValueFilters != null) {
0115:                    new_valuefilters = mValueFilters.clone();
0116:                }
0117:                new_parser.init(mTemplateFactory, mIdentifier, mConfigs,
0118:                        mExtension, new_blockfilters, new_valuefilters);
0119:
0120:                return new_parser;
0121:            }
0122:
0123:            public boolean equals(Object object) {
0124:                if (object == this ) {
0125:                    return true;
0126:                }
0127:
0128:                if (null == object) {
0129:                    return false;
0130:                }
0131:
0132:                if (!(object instanceof  Parser)) {
0133:                    return false;
0134:                }
0135:
0136:                Parser other_parser = (Parser) object;
0137:                if (!Arrays.equals(other_parser.mConfigs, this .mConfigs)) {
0138:                    return false;
0139:                }
0140:                if (!other_parser.mIdentifier.equals(this .mIdentifier)) {
0141:                    return false;
0142:                }
0143:                if (!other_parser.mExtension.equals(this .mExtension)) {
0144:                    return false;
0145:                }
0146:
0147:                if (null == other_parser.mBlockFilters
0148:                        && this .mBlockFilters != null
0149:                        || other_parser.mBlockFilters != null
0150:                        && null == this .mBlockFilters) {
0151:                    return false;
0152:                }
0153:                if (other_parser.mBlockFilters != null
0154:                        && this .mBlockFilters != null) {
0155:                    if (other_parser.mBlockFilters.length != this .mBlockFilters.length) {
0156:                        return false;
0157:                    }
0158:
0159:                    for (int i = 0; i < other_parser.mBlockFilters.length; i++) {
0160:                        if (!other_parser.mBlockFilters[i].pattern().equals(
0161:                                this .mBlockFilters[i].pattern())) {
0162:                            return false;
0163:                        }
0164:                    }
0165:                }
0166:
0167:                if (null == other_parser.mValueFilters
0168:                        && this .mValueFilters != null
0169:                        || other_parser.mValueFilters != null
0170:                        && null == this .mValueFilters) {
0171:                    return false;
0172:                }
0173:                if (other_parser.mValueFilters != null
0174:                        && this .mValueFilters != null) {
0175:                    if (other_parser.mValueFilters.length != this .mValueFilters.length) {
0176:                        return false;
0177:                    }
0178:
0179:                    for (int i = 0; i < other_parser.mValueFilters.length; i++) {
0180:                        if (!other_parser.mValueFilters[i].pattern().equals(
0181:                                this .mValueFilters[i].pattern())) {
0182:                            return false;
0183:                        }
0184:                    }
0185:                }
0186:
0187:                return true;
0188:            }
0189:
0190:            public Parsed parse(String name, String encoding,
0191:                    TemplateTransformer transformer) throws TemplateException {
0192:                if (null == name)
0193:                    throw new IllegalArgumentException("name can't be null.");
0194:
0195:                URL resource = resolve(name);
0196:                if (null == resource) {
0197:                    throw new TemplateNotFoundException(name, null);
0198:                }
0199:
0200:                return parse(prepare(name, resource), encoding, transformer);
0201:            }
0202:
0203:            public URL resolve(String name) {
0204:                if (null == name)
0205:                    throw new IllegalArgumentException("name can't be null.");
0206:
0207:                if (0 == name.indexOf(getPackage())) {
0208:                    name = name.substring(getPackage().length());
0209:                }
0210:                name = name.replace('.', '/') + mExtension;
0211:
0212:                URL resource = mTemplateFactory.getResourceFinder()
0213:                        .getResource(name);
0214:                if (null == resource) {
0215:                    resource = mTemplateFactory.getResourceFinder()
0216:                            .getResource(DEFAULT_TEMPLATES_PATH + name);
0217:                }
0218:
0219:                return resource;
0220:            }
0221:
0222:            public String getPackage() {
0223:                return mPackageName;
0224:            }
0225:
0226:            String escapeClassname(String name) {
0227:                assert name != null;
0228:
0229:                if (name.equals(mAmbiguousName)) {
0230:                    throw new AmbiguousTemplateNameException(name);
0231:                }
0232:
0233:                if (name.endsWith(mExtension)) {
0234:                    name = name.substring(0, name.length() - mExtensionLength);
0235:                }
0236:
0237:                char[] name_chars = name.toCharArray();
0238:                int char_code;
0239:                for (int i = 0; i < name_chars.length; i++) {
0240:                    char_code = name_chars[i];
0241:                    if ((char_code >= 48 && char_code <= 57)
0242:                            || (char_code >= 65 && char_code <= 90)
0243:                            || (char_code >= 97 && char_code <= 122)
0244:                            || char_code == 46) {
0245:                        continue;
0246:                    }
0247:
0248:                    if (char_code == '/' || char_code == '\\') {
0249:                        name_chars[i] = '.';
0250:                    } else {
0251:                        name_chars[i] = '_';
0252:                    }
0253:                }
0254:
0255:                return new String(name_chars);
0256:            }
0257:
0258:            public Parsed prepare(String name, URL resource) {
0259:                if (null == name)
0260:                    throw new IllegalArgumentException("name can't be null.");
0261:                if (null == resource)
0262:                    throw new IllegalArgumentException(
0263:                            "resource can't be null.");
0264:
0265:                String template_name = name;
0266:                Parsed template_parsed = new Parsed(this );
0267:                if (0 == template_name.indexOf(getPackage())) {
0268:                    template_name = name.substring(getPackage().length());
0269:                }
0270:
0271:                String class_name = template_name;
0272:                String subpackage = "";
0273:                int package_seperator = template_name.lastIndexOf(".");
0274:                if (package_seperator != -1) {
0275:                    subpackage = "."
0276:                            + template_name.substring(0, package_seperator);
0277:                    class_name = template_name.substring(package_seperator + 1);
0278:                }
0279:                template_parsed.setTemplateName(template_name);
0280:                template_parsed.setPackage(getPackage().substring(0,
0281:                        getPackage().length() - 1)
0282:                        + subpackage);
0283:                template_parsed.setClassName(escapeClassname(class_name));
0284:                template_parsed.setResource(resource);
0285:
0286:                return template_parsed;
0287:            }
0288:
0289:            Parsed parse(Parsed parsed, String encoding,
0290:                    TemplateTransformer transformer) throws TemplateException {
0291:                assert parsed != null;
0292:
0293:                if (null == encoding) {
0294:                    encoding = RifeConfig.Template.getDefaultEncoding();
0295:                }
0296:
0297:                // get the resource of the template file
0298:                URL resource = parsed.getResource();
0299:
0300:                // obtain the content of the template file
0301:                StringBuilder content_buffer = getContent(parsed
0302:                        .getTemplateName(), parsed, resource, encoding,
0303:                        transformer);
0304:
0305:                // replace the included templates
0306:                Stack<String> previous_includes = new Stack<String>();
0307:                previous_includes.push(parsed.getFullClassName());
0308:                replaceIncludeTags(parsed, content_buffer, previous_includes,
0309:                        encoding, transformer);
0310:                previous_includes.pop();
0311:
0312:                // process the blocks and values
0313:                String content = content_buffer.toString();
0314:                parseBlocks(parsed, content);
0315:                parsed.setFilteredBlocks(filterTags(mBlockFilters, parsed
0316:                        .getBlockIds()));
0317:                parsed.setFilteredValues(filterTags(mValueFilters, parsed
0318:                        .getValueIds()));
0319:
0320:                assert parsed.getBlocks().size() >= 1;
0321:
0322:                return parsed;
0323:            }
0324:
0325:            private StringBuilder getContent(String templateName,
0326:                    Parsed parsed, URL resource, String encoding,
0327:                    TemplateTransformer transformer) throws TemplateException {
0328:                if (null == transformer) {
0329:                    return getFileContent(resource, encoding);
0330:                } else {
0331:                    return getTransformedContent(templateName, parsed,
0332:                            resource, encoding, transformer);
0333:                }
0334:            }
0335:
0336:            private StringBuilder getFileContent(URL resource, String encoding)
0337:                    throws TemplateException {
0338:                assert resource != null;
0339:
0340:                String content = null;
0341:
0342:                try {
0343:                    content = mTemplateFactory.getResourceFinder().getContent(
0344:                            resource, encoding);
0345:                } catch (ResourceFinderErrorException e) {
0346:                    throw new GetContentErrorException(resource
0347:                            .toExternalForm(), e);
0348:                }
0349:
0350:                return new StringBuilder(content);
0351:            }
0352:
0353:            private StringBuilder getTransformedContent(String templateName,
0354:                    Parsed parsed, URL resource, String encoding,
0355:                    TemplateTransformer transformer) throws TemplateException {
0356:                assert resource != null;
0357:
0358:                ByteArrayOutputStream result = new ByteArrayOutputStream();
0359:
0360:                // transform the content
0361:                transformer.setResourceFinder(mTemplateFactory
0362:                        .getResourceFinder());
0363:                Collection<URL> dependencies = transformer.transform(
0364:                        templateName, resource, result, encoding);
0365:                // get the dependencies and their modification times
0366:                if (dependencies != null && dependencies.size() > 0) {
0367:                    long modification_time = 0;
0368:                    for (URL dependency_resource : dependencies) {
0369:                        try {
0370:                            modification_time = transformer.getResourceFinder()
0371:                                    .getModificationTime(dependency_resource);
0372:                        } catch (ResourceFinderErrorException e) {
0373:                            // if there was trouble in obtaining the modification time, just set
0374:                            // it to 0 so that the dependency will always be outdated
0375:                            modification_time = 0;
0376:                        }
0377:
0378:                        if (modification_time > 0) {
0379:                            parsed.addDependency(dependency_resource, new Long(
0380:                                    modification_time));
0381:                        }
0382:                    }
0383:                }
0384:                // set the modification state so that different filter configurations
0385:                // will reload the template, not only modifications to the dependencies
0386:                parsed.setModificationState(transformer.getState());
0387:
0388:                // convert the result to a StringBuilder
0389:                try {
0390:                    if (null == encoding) {
0391:                        if (null == transformer.getEncoding()) {
0392:                            return new StringBuilder(result.toString());
0393:                        } else {
0394:                            return new StringBuilder(result
0395:                                    .toString(transformer.getEncoding()));
0396:                        }
0397:                    } else {
0398:                        return new StringBuilder(result.toString(encoding));
0399:                    }
0400:                } catch (UnsupportedEncodingException e) {
0401:                    throw new TransformedResultConversionException(resource
0402:                            .toExternalForm(), e);
0403:                }
0404:            }
0405:
0406:            public static long getModificationTime(
0407:                    ResourceFinder resourceFinder, URL resource)
0408:                    throws TemplateException {
0409:                if (null == resource)
0410:                    throw new IllegalArgumentException(
0411:                            "resource can't be null.");
0412:
0413:                long modification_time = -1;
0414:
0415:                try {
0416:                    modification_time = resourceFinder
0417:                            .getModificationTime(resource);
0418:                } catch (ResourceFinderErrorException e) {
0419:                    throw new ModificationTimeErrorException(resource
0420:                            .toExternalForm(), e);
0421:                }
0422:
0423:                return modification_time;
0424:            }
0425:
0426:            private int forwardToNextWhitespace(int start, String content) {
0427:                // forward until the first non whitespace character
0428:                int i = start;
0429:                for (; i < content.length(); i++) {
0430:                    if (!Character.isWhitespace(content.charAt(i))) {
0431:                        break;
0432:                    }
0433:                }
0434:
0435:                return i;
0436:            }
0437:
0438:            /**
0439:             * Get the next indices of a search string in the content by taking
0440:             * backslash escaping into account and skipping over those search
0441:             * strings that are escaped by it. If fixed parts are provided it will also
0442:             * ensure that the search string is followed by them and the text in between
0443:             * is only whitespace.
0444:             */
0445:            private TagMatch getEscapedIndex(String content, String search,
0446:                    int start, ConfigPart... fixedParts) {
0447:                TagMatch match = new TagMatch();
0448:
0449:                int begin_index = content.indexOf(search, start);
0450:
0451:                if (begin_index != -1) {
0452:                    int ending_index = -1;
0453:                    int last_match = begin_index;
0454:                    int last_ending_index = -1;
0455:                    while (begin_index != -1) {
0456:                        match.clear();
0457:
0458:                        match.addMatch(begin_index);
0459:
0460:                        if (begin_index > 0
0461:                                && '\\' == content.charAt(begin_index - 1) && // check for a first escaping backslash
0462:                                (begin_index < 2 || content
0463:                                        .charAt(begin_index - 2) != '\\')) // check if that one hasn't been escaped itself
0464:                        {
0465:                            begin_index = -1;
0466:                        }
0467:                        // if fixed parts have been provided, ensure that they are available
0468:                        // after the last match and that the text between them is only
0469:                        // whitespace
0470:                        else if (fixedParts != null && fixedParts.length > 0) {
0471:                            last_ending_index = begin_index + search.length();
0472:                            for (ConfigPart fixed_part : fixedParts) {
0473:                                if (0 == fixed_part.length()) {
0474:                                    match.addMatch(forwardToNextWhitespace(
0475:                                            last_ending_index, content));
0476:                                    match.addFixedPartMatched(true);
0477:                                    continue;
0478:                                }
0479:
0480:                                ending_index = content.indexOf(fixed_part
0481:                                        .getText(), last_ending_index);
0482:                                String seperating_text = null;
0483:                                String seperating_text_trim = null;
0484:                                if (ending_index != -1) {
0485:                                    seperating_text = content.substring(
0486:                                            last_ending_index, ending_index);
0487:                                    seperating_text_trim = seperating_text
0488:                                            .trim();
0489:                                }
0490:                                if (-1 == ending_index
0491:                                        || seperating_text_trim.length() != 0) {
0492:                                    if (fixed_part.isOptional()
0493:                                            && (null == seperating_text || seperating_text
0494:                                                    .indexOf(seperating_text_trim) > 0)) {
0495:                                        match.addMatch(forwardToNextWhitespace(
0496:                                                last_ending_index, content));
0497:                                        match.addFixedPartMatched(false);
0498:                                        continue;
0499:                                    } else {
0500:                                        begin_index = -1;
0501:                                        break;
0502:                                    }
0503:                                }
0504:
0505:                                last_ending_index = ending_index
0506:                                        + fixed_part.length();
0507:                                match.addMatch(last_ending_index);
0508:                                match.addFixedPartMatched(true);
0509:                            }
0510:                        }
0511:
0512:                        // continue searching if the match wasn't successful
0513:                        if (-1 == begin_index) {
0514:                            begin_index = content.indexOf(search,
0515:                                    last_match + 1);
0516:                            last_match = begin_index;
0517:                            continue;
0518:                        }
0519:
0520:                        return match;
0521:                    }
0522:                }
0523:
0524:                // return negative result
0525:                return null;
0526:            }
0527:
0528:            /**
0529:             * Construct an array with only the configurations that need to be unescaped
0530:             */
0531:            private Parser.Config[] getUnescapeConfigs(String content) {
0532:                // iterate over the supported parser configurations to find the ones
0533:                // that are unused
0534:                List<Parser.Config> configs_list = new ArrayList<Parser.Config>();
0535:                for (Parser.Config config : mConfigs) {
0536:                    if (content.indexOf(config.mUnescapeStart.getText()) != -1) {
0537:                        configs_list.add(config);
0538:                    }
0539:                }
0540:
0541:                Parser.Config[] configs = null;
0542:                if (configs_list.size() > 0) {
0543:                    // make a config array, containing only the used configs
0544:                    configs = new Parser.Config[configs_list.size()];
0545:                    configs_list.toArray(configs);
0546:                }
0547:
0548:                return configs;
0549:            }
0550:
0551:            /**
0552:             * Removes the single escape backslash character from content
0553:             */
0554:            private String unescapePart(Parser.Config[] configs, String part) {
0555:                if (configs != null) {
0556:                    for (Parser.Config config : configs) {
0557:                        // minor optimization to only do regexp unescape matching when a
0558:                        // matching possibility is present through a quick lookup
0559:                        if (part.indexOf(config.mUnescapeStart.getText()) != -1) {
0560:                            part = config.mUnescapePattern.matcher(part)
0561:                                    .replaceAll("$1");
0562:                        }
0563:                    }
0564:                }
0565:
0566:                part = removeTrailingDoubleEscape(part, 0, part.length());
0567:
0568:                return part;
0569:            }
0570:
0571:            /**
0572:             * Replaces the double escaping backslashes from the end of block content
0573:             * by a single backslash
0574:             */
0575:            private String removeTrailingDoubleEscape(String content,
0576:                    int begin, int end) {
0577:                if (end >= 2 && '\\' == content.charAt(end - 1)
0578:                        && '\\' == content.charAt(end - 2)) {
0579:                    return content.substring(begin, end - 1);
0580:                } else {
0581:                    return content.substring(begin, end);
0582:                }
0583:            }
0584:
0585:            /**
0586:             * Looks for the first match of a config part text in the text content. If
0587:             * a part doesn't match and is optional, the next one will be tried. If the
0588:             * part is empty, the next one will always be tried immediately.
0589:             **/
0590:            private ConfigPartMatch getFirstFoundPartIndex(String content,
0591:                    int startIndex, ConfigPart... parts) {
0592:                for (ConfigPart part : parts) {
0593:                    if (part.length() != 0) {
0594:                        int result = content
0595:                                .indexOf(part.getText(), startIndex);
0596:                        if (result != -1) {
0597:                            return new ConfigPartMatch(result, part);
0598:                        } else if (!part.isOptional()) {
0599:                            return ConfigPartMatch.NO_MATCH;
0600:                        }
0601:                    }
0602:                }
0603:
0604:                return ConfigPartMatch.NO_MATCH;
0605:            }
0606:
0607:            private void replaceIncludeTags(Parsed parsed,
0608:                    StringBuilder content, Stack<String> previousIncludes,
0609:                    String encoding, TemplateTransformer transformer)
0610:                    throws TemplateException {
0611:                assert parsed != null;
0612:                assert content != null;
0613:                assert previousIncludes != null;
0614:
0615:                TagMatch tag_match = null;
0616:                ConfigPartMatch part_match = null;
0617:
0618:                int previous_index = 0;
0619:                int begin_start_index = 0;
0620:                int includename_end_index = 0;
0621:                int term_start_index = 0;
0622:                int tag_end_index = 0;
0623:                int includename_begin_index = 0;
0624:
0625:                boolean begin_isquoted = false;
0626:
0627:                String included_template_name = null;
0628:                URL included_template_resource = null;
0629:                Parsed included_template_parsed = null;
0630:                StringBuilder included_template_content = null;
0631:
0632:                // process the included files
0633:                // and iterate over the support parser configurations
0634:                for (Parser.Config config : mConfigs) {
0635:                    do {
0636:                        // find the begin position of the include tag
0637:                        tag_match = getEscapedIndex(content.toString(),
0638:                                config.mTagStart.getText(), previous_index,
0639:                                config.mIncludeTag,
0640:                                config.mStringDelimiterBegin);
0641:
0642:                        if (tag_match != null) {
0643:                            begin_start_index = tag_match.getMatch(0);
0644:
0645:                            // find the begin of the filename
0646:                            includename_begin_index = tag_match.getMatch(2);
0647:                            begin_isquoted = tag_match.didFixedPartMatch(1);
0648:                            part_match = null;
0649:                            includename_end_index = -1;
0650:
0651:                            // find the string delimiter
0652:                            // get the string delimiter that marks the end of the value id
0653:                            int includename_end_offset = 0;
0654:                            if (begin_isquoted) {
0655:                                part_match = getFirstFoundPartIndex(content
0656:                                        .toString(), includename_begin_index,
0657:                                        config.mStringDelimiterEnd);
0658:                            }
0659:
0660:                            if (part_match != null && part_match.mPart != null) {
0661:                                includename_end_index = part_match.mIndex;
0662:                                includename_end_offset = part_match.mPart
0663:                                        .length();
0664:                            }
0665:                            // ensure that a name that started out quoted is already terminated with a delimiter
0666:                            else if (begin_isquoted && null == part_match.mPart) {
0667:                                throw new AttributeNotEndedException(parsed
0668:                                        .getClassName(), StringUtils
0669:                                        .getDocumentPosition(
0670:                                                content.toString(),
0671:                                                includename_begin_index),
0672:                                        config.mIncludeTag.getText(), "name");
0673:                            } else {
0674:                                int short_index = content.indexOf(
0675:                                        config.mTagShortTerm.getText(),
0676:                                        includename_begin_index);
0677:                                includename_end_offset = 0;
0678:                                includename_end_index = backtrackTillFirstNonWhitespace(
0679:                                        content.toString(), short_index);
0680:                            }
0681:
0682:                            // check if the include name was ended
0683:                            if (-1 == includename_end_index)
0684:                                break;
0685:
0686:                            // obtain the filename
0687:                            included_template_name = content.substring(
0688:                                    includename_begin_index,
0689:                                    includename_end_index);
0690:
0691:                            // ensure that an end delimiter corresponds to a start delimiter
0692:                            if (!begin_isquoted
0693:                                    && included_template_name
0694:                                            .endsWith(config.mStringDelimiterEnd
0695:                                                    .getText())) {
0696:                                throw new AttributeWronglyEndedException(
0697:                                        parsed.getClassName(),
0698:                                        StringUtils
0699:                                                .getDocumentPosition(
0700:                                                        content.toString(),
0701:                                                        includename_end_index
0702:                                                                + includename_end_offset
0703:                                                                - config.mStringDelimiterEnd
0704:                                                                        .length()),
0705:                                        config.mIncludeTag.getText(), "name");
0706:                            }
0707:
0708:                            // get the first tag ending
0709:                            term_start_index = content.indexOf(
0710:                                    config.mTagShortTerm.getText(),
0711:                                    includename_end_index
0712:                                            + includename_end_offset);
0713:
0714:                            // ensure that the tag was propertly ended
0715:                            if (-1 == term_start_index)
0716:                                break;
0717:
0718:                            // check that there's only whitespace between the delimiter and the start of the termination tag
0719:                            if (content.substring(
0720:                                    includename_end_index
0721:                                            + includename_end_offset,
0722:                                    term_start_index).trim().length() > 0) {
0723:                                throw new TagBadlyTerminatedException(parsed
0724:                                        .getClassName(), StringUtils
0725:                                        .getDocumentPosition(
0726:                                                content.toString(),
0727:                                                term_start_index),
0728:                                        config.mIncludeTag.getText(),
0729:                                        included_template_name);
0730:                            }
0731:
0732:                            // calculate the index of the end of the complete tag
0733:                            tag_end_index = term_start_index
0734:                                    + config.mTagShortTermLength;
0735:
0736:                            // obtain the parser that will be used to get the included content
0737:                            Parser include_parser = this ;
0738:                            // check if the included template references another template type
0739:                            int doublecolon_index = included_template_name
0740:                                    .indexOf(':');
0741:                            if (doublecolon_index != -1) {
0742:                                String template_type = included_template_name
0743:                                        .substring(0, doublecolon_index);
0744:                                if (!template_type.equals(mTemplateFactory
0745:                                        .toString())) {
0746:                                    TemplateFactory factory = TemplateFactory
0747:                                            .getFactory(template_type);
0748:                                    include_parser = factory.getParser();
0749:                                    included_template_name = included_template_name
0750:                                            .substring(doublecolon_index + 1);
0751:                                }
0752:                            }
0753:
0754:                            included_template_resource = include_parser
0755:                                    .resolve(included_template_name);
0756:                            if (null == included_template_resource) {
0757:                                throw new IncludeNotFoundException(parsed
0758:                                        .getClassName(), StringUtils
0759:                                        .getDocumentPosition(
0760:                                                content.toString(),
0761:                                                begin_start_index),
0762:                                        included_template_name);
0763:                            }
0764:                            included_template_parsed = include_parser.prepare(
0765:                                    included_template_name,
0766:                                    included_template_resource);
0767:
0768:                            // check for circular references
0769:                            if (previousIncludes
0770:                                    .contains(included_template_parsed
0771:                                            .getFullClassName())) {
0772:                                throw new CircularIncludesException(parsed
0773:                                        .getClassName(), StringUtils
0774:                                        .getDocumentPosition(
0775:                                                content.toString(),
0776:                                                begin_start_index),
0777:                                        included_template_name,
0778:                                        previousIncludes);
0779:                            }
0780:
0781:                            // parse the included template's include tags too
0782:                            included_template_content = include_parser
0783:                                    .getContent(included_template_name, parsed,
0784:                                            included_template_parsed
0785:                                                    .getResource(), encoding,
0786:                                            transformer);
0787:                            previousIncludes.push(included_template_parsed
0788:                                    .getFullClassName());
0789:                            replaceIncludeTags(included_template_parsed,
0790:                                    included_template_content,
0791:                                    previousIncludes, encoding, transformer);
0792:                            previousIncludes.pop();
0793:
0794:                            // replace the tag with the included file's content
0795:                            // if there's a double escaping backslash before the tag, it
0796:                            // will also be unescaped
0797:                            if (begin_start_index >= 2
0798:                                    && '\\' == content
0799:                                            .charAt(begin_start_index - 1)
0800:                                    && '\\' == content
0801:                                            .charAt(begin_start_index - 2)) {
0802:                                content.replace(begin_start_index - 1,
0803:                                        tag_end_index,
0804:                                        included_template_content.toString());
0805:                            } else {
0806:                                content.replace(begin_start_index,
0807:                                        tag_end_index,
0808:                                        included_template_content.toString());
0809:                            }
0810:
0811:                            // retain the link to this include file for optional later modification time checking
0812:                            parsed.addDependency(included_template_parsed);
0813:
0814:                            // add the dependencies of the included template too
0815:                            Map<URL, Long> included_dependencies = included_template_parsed
0816:                                    .getDependencies();
0817:                            for (Map.Entry<URL, Long> included_dependency : included_dependencies
0818:                                    .entrySet()) {
0819:                                parsed.addDependency(included_dependency
0820:                                        .getKey(), included_dependency
0821:                                        .getValue());
0822:                            }
0823:
0824:                            // continue the search after this tag
0825:                            previous_index = begin_start_index;
0826:                        }
0827:                    } while (tag_match != null);
0828:                }
0829:            }
0830:
0831:            private TagMatch getFirstMatch(TagMatch... matches) {
0832:                if (null == matches || 0 == matches.length) {
0833:                    return null;
0834:                }
0835:
0836:                TagMatch first_match = null;
0837:                for (TagMatch match : matches) {
0838:                    if (match != null) {
0839:                        int candidate = match.getMatch(0);
0840:                        if (candidate != -1) {
0841:                            if (null == first_match) {
0842:                                first_match = match;
0843:                            } else {
0844:                                if (candidate <= first_match.getMatch(0)) {
0845:                                    first_match = match;
0846:                                }
0847:                            }
0848:                        }
0849:                    }
0850:                }
0851:
0852:                return first_match;
0853:            }
0854:
0855:            private int getFirstMatch(int... candidates) {
0856:                if (null == candidates || 0 == candidates.length) {
0857:                    return -1;
0858:                }
0859:
0860:                int first_candidate = -1;
0861:                for (int candidate : candidates) {
0862:                    if (candidate != -1) {
0863:                        if (-1 == first_candidate) {
0864:                            first_candidate = candidate;
0865:                        } else {
0866:                            if (candidate <= first_candidate) {
0867:                                first_candidate = candidate;
0868:                            }
0869:                        }
0870:                    }
0871:                }
0872:
0873:                return first_candidate;
0874:            }
0875:
0876:            private int backtrackTillFirstNonWhitespace(String content,
0877:                    int index) {
0878:                if (-1 == index) {
0879:                    return -1;
0880:                } else {
0881:                    do {
0882:                        index--;
0883:
0884:                        if (!Character.isWhitespace(content.charAt(index))) {
0885:                            break;
0886:                        }
0887:                    } while (index >= 0);
0888:
0889:                    return index + 1;
0890:                }
0891:            }
0892:
0893:            private void parseBlocks(Parsed parsed, String content)
0894:                    throws TemplateException {
0895:                assert parsed != null;
0896:                assert content != null;
0897:
0898:                LinkedHashMap<String, StringBuilder> blocks = new LinkedHashMap<String, StringBuilder>();
0899:                blocks.put("", new StringBuilder(""));
0900:
0901:                // iterate over the supported parser configurations to find the ones
0902:                // that are unused
0903:                List<Parser.Config> block_configs_list = new ArrayList<Parser.Config>();
0904:                for (Parser.Config config : mConfigs) {
0905:                    if (getEscapedIndex(content, config.mTagStart.getText(), 0,
0906:                            config.mBlockTag, config.mStringDelimiterBegin) != null
0907:                            || getEscapedIndex(content, config.mTagStart
0908:                                    .getText(), 0, config.mBlockvalueTag,
0909:                                    config.mStringDelimiterBegin) != null
0910:                            || getEscapedIndex(content, config.mTagStart
0911:                                    .getText(), 0, config.mBlockappendTag,
0912:                                    config.mStringDelimiterBegin) != null
0913:                            || getEscapedIndex(content, config.mTagTerm
0914:                                    .getText(), 0, config.mBlockTag,
0915:                                    config.mTagEnd) != null
0916:                            || getEscapedIndex(content, config.mTagTerm
0917:                                    .getText(), 0, config.mBlockvalueTag,
0918:                                    config.mTagEnd) != null
0919:                            || getEscapedIndex(content, config.mTagTerm
0920:                                    .getText(), 0, config.mBlockappendTag,
0921:                                    config.mTagEnd) != null
0922:                            || getEscapedIndex(content, config.mTagStart
0923:                                    .getText(), 0, config.mCommentTag,
0924:                                    config.mTagEnd) != null
0925:                            || getEscapedIndex(content, config.mTagTerm
0926:                                    .getText(), 0, config.mCommentTag,
0927:                                    config.mTagEnd) != null) {
0928:                        block_configs_list.add(config);
0929:                    }
0930:                }
0931:
0932:                Parser.Config[] configs = null;
0933:                int previous_index = 0;
0934:                if (block_configs_list.size() > 0) {
0935:                    // make a config array, containing only the used configs
0936:                    configs = new Parser.Config[block_configs_list.size()];
0937:                    block_configs_list.toArray(configs);
0938:
0939:                    // setup the parser variables
0940:                    Stack<String> block_ids = new Stack<String>();
0941:                    Stack<ConfigPart> block_types = new Stack<ConfigPart>();
0942:                    Stack<Parser.Config> block_configs = new Stack<Parser.Config>();
0943:
0944:                    // create the root block which is the anonymous main content
0945:                    block_ids.push("");
0946:                    block_types.push(new ConfigPart(PartType.CONTENT,
0947:                            "CONTENT", false));
0948:                    block_configs.push(null);
0949:
0950:                    TagMatch match1 = null;
0951:                    TagMatch match2 = null;
0952:                    TagMatch match3 = null;
0953:                    TagMatch match4 = null;
0954:                    TagMatch first_match = null;
0955:
0956:                    int begin_start_index = 0;
0957:                    int delimiter_end_index = 0;
0958:                    int begin_end_index = 0;
0959:                    int term_start_index = 0;
0960:                    int term_end_index = 0;
0961:                    int blockid_start_index = 0;
0962:                    int blockid_end_index = 0;
0963:
0964:                    int[] begin_start_indices = new int[configs.length];
0965:                    int[] delimiter_end_indices = new int[configs.length];
0966:                    boolean[] begin_isblockvalue_flags = new boolean[configs.length];
0967:                    boolean[] begin_isblockappend_flags = new boolean[configs.length];
0968:                    boolean[] begin_iscomment_flags = new boolean[configs.length];
0969:                    boolean[] begin_isquoted_flags = new boolean[configs.length];
0970:                    int[] term_start_indices = new int[configs.length];
0971:                    int[] term_end_indices = new int[configs.length];
0972:                    boolean[] term_isblockvalue_flags = new boolean[configs.length];
0973:                    boolean[] term_isblockappend_flags = new boolean[configs.length];
0974:                    boolean[] term_iscomment_flags = new boolean[configs.length];
0975:
0976:                    String leftover_content = null;
0977:                    String blockid = null;
0978:
0979:                    Parser.Config config_begin = null;
0980:                    Parser.Config config_term = null;
0981:
0982:                    boolean begin_isblockvalue = false;
0983:                    boolean begin_isblockappend = false;
0984:                    boolean begin_iscomment = false;
0985:                    boolean begin_isquoted = false;
0986:                    boolean term_isblockvalue = false;
0987:                    boolean term_isblockappend = false;
0988:                    boolean term_iscomment = false;
0989:
0990:                    // process the block and content
0991:                    do {
0992:                        {
0993:                            String block_id_peek = block_ids.peek();
0994:
0995:                            // iterate over the supported parser configurations to find all
0996:                            // start tag beginnings
0997:                            for (int i = 0; i < configs.length; i++) {
0998:                                if (null == block_id_peek) {
0999:                                    first_match = null;
1000:                                } else {
1001:                                    match1 = getEscapedIndex(content,
1002:                                            configs[i].mTagStart.getText(),
1003:                                            previous_index,
1004:                                            configs[i].mBlockTag,
1005:                                            configs[i].mStringDelimiterBegin);
1006:                                    match2 = getEscapedIndex(content,
1007:                                            configs[i].mTagStart.getText(),
1008:                                            previous_index,
1009:                                            configs[i].mBlockvalueTag,
1010:                                            configs[i].mStringDelimiterBegin);
1011:                                    match3 = getEscapedIndex(content,
1012:                                            configs[i].mTagStart.getText(),
1013:                                            previous_index,
1014:                                            configs[i].mBlockappendTag,
1015:                                            configs[i].mStringDelimiterBegin);
1016:                                    match4 = getEscapedIndex(content,
1017:                                            configs[i].mTagStart.getText(),
1018:                                            previous_index,
1019:                                            configs[i].mCommentTag,
1020:                                            configs[i].mTagEnd);
1021:                                    first_match = getFirstMatch(match1, match2,
1022:                                            match3, match4);
1023:                                }
1024:
1025:                                if (null == first_match) {
1026:                                    begin_start_indices[i] = -1;
1027:                                    begin_isblockvalue_flags[i] = false;
1028:                                    begin_isblockappend_flags[i] = false;
1029:                                    begin_iscomment_flags[i] = false;
1030:                                    begin_isquoted_flags[i] = false;
1031:                                    delimiter_end_indices[i] = -1;
1032:                                } else {
1033:                                    begin_start_indices[i] = first_match
1034:                                            .getMatch(0);
1035:                                    begin_isblockvalue_flags[i] = (first_match == match2);
1036:                                    begin_isblockappend_flags[i] = (first_match == match3);
1037:                                    begin_iscomment_flags[i] = (first_match == match4);
1038:                                    begin_isquoted_flags[i] = first_match
1039:                                            .didFixedPartMatch(1);
1040:                                    delimiter_end_indices[i] = first_match
1041:                                            .getMatch(2);
1042:                                }
1043:                            }
1044:
1045:                            // iterate over the supported parser configurations all term tag
1046:                            // beginnings
1047:                            for (int i = 0; i < configs.length; i++) {
1048:                                if (null == block_id_peek) {
1049:                                    match1 = null;
1050:                                    match2 = null;
1051:                                    match3 = null;
1052:                                } else {
1053:                                    match1 = getEscapedIndex(content,
1054:                                            configs[i].mTagTerm.getText(),
1055:                                            previous_index,
1056:                                            configs[i].mBlockTag,
1057:                                            configs[i].mTagEnd);
1058:                                    match2 = getEscapedIndex(content,
1059:                                            configs[i].mTagTerm.getText(),
1060:                                            previous_index,
1061:                                            configs[i].mBlockvalueTag,
1062:                                            configs[i].mTagEnd);
1063:                                    match3 = getEscapedIndex(content,
1064:                                            configs[i].mTagTerm.getText(),
1065:                                            previous_index,
1066:                                            configs[i].mBlockappendTag,
1067:                                            configs[i].mTagEnd);
1068:                                }
1069:                                match4 = getEscapedIndex(content,
1070:                                        configs[i].mTagTerm.getText(),
1071:                                        previous_index, configs[i].mCommentTag,
1072:                                        configs[i].mTagEnd);
1073:                                first_match = getFirstMatch(match1, match2,
1074:                                        match3, match4);
1075:
1076:                                if (null == first_match) {
1077:                                    term_start_indices[i] = -1;
1078:                                    term_isblockvalue_flags[i] = false;
1079:                                    term_isblockappend_flags[i] = false;
1080:                                    term_iscomment_flags[i] = false;
1081:                                    term_end_indices[i] = -1;
1082:                                } else {
1083:                                    term_start_indices[i] = first_match
1084:                                            .getMatch(0);
1085:                                    term_isblockvalue_flags[i] = (first_match == match2);
1086:                                    term_isblockappend_flags[i] = (first_match == match3);
1087:                                    term_iscomment_flags[i] = (first_match == match4);
1088:                                    term_end_indices[i] = first_match
1089:                                            .getMatch(2);
1090:                                }
1091:                            }
1092:                        }
1093:
1094:                        // find the start position of the next block begin tags by comparing
1095:                        // which is the earliest start tag beginning
1096:                        config_begin = null;
1097:                        begin_start_index = -1;
1098:                        for (int i = 0; i < begin_start_indices.length; i++) {
1099:                            if (begin_start_indices[i] != -1
1100:                                    && (-1 == begin_start_index || begin_start_indices[i] < begin_start_index)) {
1101:                                begin_start_index = begin_start_indices[i];
1102:                                begin_isblockvalue = begin_isblockvalue_flags[i];
1103:                                begin_isblockappend = begin_isblockappend_flags[i];
1104:                                begin_iscomment = begin_iscomment_flags[i];
1105:                                begin_isquoted = begin_isquoted_flags[i];
1106:                                delimiter_end_index = delimiter_end_indices[i];
1107:                                config_begin = configs[i];
1108:                            }
1109:                        }
1110:
1111:                        // find the start position of the next block term tags by comparing
1112:                        // which is the earliest term tag beginning
1113:                        config_term = null;
1114:                        term_start_index = -1;
1115:                        term_end_index = -1;
1116:                        for (int i = 0; i < term_start_indices.length; i++) {
1117:                            if (term_start_indices[i] != -1
1118:                                    && (-1 == term_start_index || term_start_indices[i] < term_start_index)) {
1119:                                term_start_index = term_start_indices[i];
1120:                                term_isblockvalue = term_isblockvalue_flags[i];
1121:                                term_isblockappend = term_isblockappend_flags[i];
1122:                                term_iscomment = term_iscomment_flags[i];
1123:                                term_end_index = term_end_indices[i];
1124:                                config_term = configs[i];
1125:                            }
1126:                        }
1127:
1128:                        // check if a begin tag was found and if the end tag comes after it
1129:                        // or the end tag has been omitted which means that the blocks are nested
1130:                        // start the new block that corresponds to the new begin tag
1131:                        if (begin_start_index != -1
1132:                                && (begin_start_index < term_start_index || -1 == term_start_index)) {
1133:                            // the text upto the beginning of this block has to be extracted
1134:                            // check if the next begin tag has been double-escaped
1135:                            leftover_content = removeTrailingDoubleEscape(
1136:                                    content, previous_index, begin_start_index);
1137:
1138:                            // and added to the preceeding block
1139:                            blockid = block_ids.peek();
1140:                            if (blockid != null) {
1141:                                blocks.get(blockid).append(leftover_content);
1142:                            }
1143:
1144:                            // check if this is a comment tag and don't extract an id in that case
1145:                            if (begin_iscomment) {
1146:                                blockid_start_index = -1;
1147:                                blockid_end_index = -1;
1148:                                begin_end_index = delimiter_end_index;
1149:                                blockid = null;
1150:
1151:                                // add this comment block to the stack of active blocks
1152:                                block_ids.push(null);
1153:                                block_types.push(config_begin.mCommentTag);
1154:                                block_configs.push(config_begin);
1155:                            } else {
1156:                                // get the begin and end position of the block's id
1157:                                blockid_start_index = delimiter_end_index;
1158:
1159:                                // get the string delimiter that marks the end of the block id
1160:                                ConfigPartMatch delimiter_end_match = null;
1161:                                if (begin_isquoted) {
1162:                                    delimiter_end_match = getFirstFoundPartIndex(
1163:                                            content, blockid_start_index,
1164:                                            config_begin.mStringDelimiterEnd);
1165:                                }
1166:
1167:                                if (delimiter_end_match != null
1168:                                        && delimiter_end_match.mPart != null) {
1169:                                    blockid_end_index = delimiter_end_match.mIndex;
1170:                                }
1171:                                // ensure that a name that started out quoted is already terminated with a delimiter
1172:                                else if (begin_isquoted
1173:                                        && null == delimiter_end_match.mPart) {
1174:                                    String tag_type;
1175:                                    if (begin_isblockvalue)
1176:                                        tag_type = config_begin.mBlockvalueTag
1177:                                                .getText();
1178:                                    else if (begin_isblockappend)
1179:                                        tag_type = config_begin.mBlockappendTag
1180:                                                .getText();
1181:                                    else
1182:                                        tag_type = config_begin.mBlockTag
1183:                                                .getText();
1184:                                    throw new AttributeNotEndedException(parsed
1185:                                            .getClassName(), StringUtils
1186:                                            .getDocumentPosition(content,
1187:                                                    blockid_start_index),
1188:                                            tag_type, "name");
1189:                                } else {
1190:                                    int long_index = content.indexOf(
1191:                                            config_begin.mTagEnd.getText(),
1192:                                            blockid_start_index);
1193:                                    int short_index = content.indexOf(
1194:                                            config_begin.mTagShortTerm
1195:                                                    .getText(),
1196:                                            blockid_start_index);
1197:
1198:                                    // backtrack until the first non whitespace character
1199:                                    blockid_end_index = backtrackTillFirstNonWhitespace(
1200:                                            content, getFirstMatch(long_index,
1201:                                                    short_index));
1202:                                }
1203:
1204:                                if (-1 == blockid_end_index) {
1205:                                    String tag_type;
1206:                                    if (begin_isblockvalue)
1207:                                        tag_type = config_begin.mBlockvalueTag
1208:                                                .getText();
1209:                                    else if (begin_isblockappend)
1210:                                        tag_type = config_begin.mBlockappendTag
1211:                                                .getText();
1212:                                    else
1213:                                        tag_type = config_begin.mBlockTag
1214:                                                .getText();
1215:                                    throw new AttributeNotEndedException(parsed
1216:                                            .getClassName(), StringUtils
1217:                                            .getDocumentPosition(content,
1218:                                                    blockid_start_index),
1219:                                            tag_type, "name");
1220:                                } else {
1221:                                    // extract the id of the block
1222:                                    blockid = content.substring(
1223:                                            blockid_start_index,
1224:                                            blockid_end_index);
1225:
1226:                                    // ensure that an end delimiter corresponds to a start delimiter
1227:                                    if (!begin_isquoted
1228:                                            && blockid
1229:                                                    .endsWith(config_begin.mStringDelimiterEnd
1230:                                                            .getText())) {
1231:                                        String tag_type;
1232:                                        if (begin_isblockvalue)
1233:                                            tag_type = config_begin.mBlockvalueTag
1234:                                                    .getText();
1235:                                        else if (begin_isblockappend)
1236:                                            tag_type = config_begin.mBlockappendTag
1237:                                                    .getText();
1238:                                        else
1239:                                            tag_type = config_begin.mBlockTag
1240:                                                    .getText();
1241:                                        throw new AttributeWronglyEndedException(
1242:                                                parsed.getClassName(),
1243:                                                StringUtils
1244:                                                        .getDocumentPosition(
1245:                                                                content,
1246:                                                                blockid_end_index
1247:                                                                        - config_begin.mStringDelimiterEnd
1248:                                                                                .length()),
1249:                                                tag_type, "name");
1250:                                    }
1251:
1252:                                    // get the index of the end of the block begin tag
1253:                                    begin_end_index = content.indexOf(
1254:                                            config_begin.mTagEnd.getText(),
1255:                                            blockid_end_index);
1256:                                    if (-1 == begin_end_index) {
1257:                                        String tag_type;
1258:                                        if (begin_isblockvalue)
1259:                                            tag_type = config_begin.mBlockvalueTag
1260:                                                    .getText();
1261:                                        else if (begin_isblockappend)
1262:                                            tag_type = config_begin.mBlockappendTag
1263:                                                    .getText();
1264:                                        else
1265:                                            tag_type = config_begin.mBlockTag
1266:                                                    .getText();
1267:                                        throw new BeginTagNotEndedException(
1268:                                                parsed.getClassName(),
1269:                                                StringUtils
1270:                                                        .getDocumentPosition(
1271:                                                                content,
1272:                                                                blockid_end_index),
1273:                                                tag_type, blockid);
1274:                                    } else {
1275:                                        // check that the begin tag termination is valid
1276:                                        if (delimiter_end_match != null
1277:                                                && delimiter_end_match.mPart != null
1278:                                                && content
1279:                                                        .substring(
1280:                                                                blockid_end_index
1281:                                                                        + delimiter_end_match.mPart
1282:                                                                                .length(),
1283:                                                                begin_end_index)
1284:                                                        .trim().length() > 0) {
1285:                                            String tag_type;
1286:                                            if (begin_isblockvalue)
1287:                                                tag_type = config_begin.mBlockvalueTag
1288:                                                        .getText();
1289:                                            else if (begin_isblockappend)
1290:                                                tag_type = config_begin.mBlockappendTag
1291:                                                        .getText();
1292:                                            else
1293:                                                tag_type = config_begin.mBlockTag
1294:                                                        .getText();
1295:                                            throw new BeginTagBadlyTerminatedException(
1296:                                                    parsed.getClassName(),
1297:                                                    StringUtils
1298:                                                            .getDocumentPosition(
1299:                                                                    content,
1300:                                                                    blockid_end_index
1301:                                                                            + config_begin.mStringDelimiterEnd
1302:                                                                                    .length()),
1303:                                                    tag_type, blockid);
1304:                                        }
1305:
1306:                                        // the start tag was correctly ended, add this block to the stack of active block ids
1307:                                        block_ids.push(blockid);
1308:                                        block_configs.push(config_begin);
1309:
1310:                                        if (begin_isblockvalue) {
1311:                                            blocks.put(blockid,
1312:                                                    new StringBuilder(""));
1313:                                            block_types
1314:                                                    .push(config_begin.mBlockvalueTag);
1315:                                            parsed.setBlockvalue(blockid);
1316:                                        } else if (begin_isblockappend) {
1317:                                            StringBuilder current_block = blocks
1318:                                                    .get(blockid);
1319:                                            if (null == current_block) {
1320:                                                current_block = new StringBuilder(
1321:                                                        "");
1322:                                                blocks.put(blockid,
1323:                                                        current_block);
1324:                                            }
1325:                                            block_types
1326:                                                    .push(config_begin.mBlockappendTag);
1327:                                            parsed.setBlockvalue(blockid);
1328:                                        } else {
1329:                                            blocks.put(blockid,
1330:                                                    new StringBuilder(""));
1331:                                            block_types
1332:                                                    .push(config_begin.mBlockTag);
1333:                                        }
1334:                                    }
1335:                                }
1336:                            }
1337:
1338:                            // continue the search after this tag
1339:                            previous_index = begin_end_index
1340:                                    + config_begin.mTagEndLength;
1341:                        }
1342:                        // the termination tag came before the end tag, this means that the current block first has
1343:                        // to be closed correctly
1344:                        else if (term_start_index > -1) {
1345:                            // the text upto the termination tag of the current block has to be extracted
1346:                            // check if the next termination tag has been double-escaped
1347:                            leftover_content = removeTrailingDoubleEscape(
1348:                                    content, previous_index, term_start_index);
1349:
1350:                            // check if a tag is actually open
1351:                            if (1 == block_ids.size()) {
1352:                                String class_name = parsed.getClassName();
1353:                                DocumentPosition doc_position = StringUtils
1354:                                        .getDocumentPosition(content,
1355:                                                term_start_index);
1356:                                if (term_isblockvalue)
1357:                                    throw new TerminatingUnopenedTagException(
1358:                                            class_name, doc_position,
1359:                                            config_term.mBlockvalueTag
1360:                                                    .getText());
1361:                                else if (term_isblockappend)
1362:                                    throw new TerminatingUnopenedTagException(
1363:                                            class_name, doc_position,
1364:                                            config_term.mBlockappendTag
1365:                                                    .getText());
1366:                                else if (term_iscomment)
1367:                                    throw new TerminatingUnopenedTagException(
1368:                                            class_name, doc_position,
1369:                                            config_term.mCommentTag.getText());
1370:                                else
1371:                                    throw new TerminatingUnopenedTagException(
1372:                                            class_name, doc_position,
1373:                                            config_term.mBlockTag.getText());
1374:                            }
1375:
1376:                            // and added to the content of the current block
1377:                            blockid = block_ids.peek();
1378:                            if (blockid != null) {
1379:                                blocks.get(blockid).append(leftover_content);
1380:
1381:                                PartType block_type = block_types.peek()
1382:                                        .getType();
1383:                                // ensure that blockvalue tags are closed with the appropriate termination tag
1384:                                if (PartType.BLOCKVALUE == block_type) {
1385:                                    if (term_iscomment) {
1386:                                        throw new MismatchedTerminationTagException(
1387:                                                parsed.getClassName(),
1388:                                                StringUtils
1389:                                                        .getDocumentPosition(
1390:                                                                content,
1391:                                                                term_start_index),
1392:                                                blockid,
1393:                                                config_term.mBlockvalueTag
1394:                                                        .getText(),
1395:                                                config_term.mCommentTag
1396:                                                        .getText());
1397:                                    } else if (term_isblockappend) {
1398:                                        throw new MismatchedTerminationTagException(
1399:                                                parsed.getClassName(),
1400:                                                StringUtils
1401:                                                        .getDocumentPosition(
1402:                                                                content,
1403:                                                                term_start_index),
1404:                                                blockid,
1405:                                                config_term.mBlockvalueTag
1406:                                                        .getText(),
1407:                                                config_term.mBlockappendTag
1408:                                                        .getText());
1409:                                    } else if (!term_isblockvalue) {
1410:                                        throw new MismatchedTerminationTagException(
1411:                                                parsed.getClassName(),
1412:                                                StringUtils
1413:                                                        .getDocumentPosition(
1414:                                                                content,
1415:                                                                term_start_index),
1416:                                                blockid,
1417:                                                config_term.mBlockvalueTag
1418:                                                        .getText(),
1419:                                                config_term.mBlockTag.getText());
1420:                                    }
1421:                                }
1422:                                // ensure that blockappend tags are closed with the appropriate termination tag
1423:                                else if (PartType.BLOCKAPPEND == block_type) {
1424:                                    if (term_iscomment) {
1425:                                        throw new MismatchedTerminationTagException(
1426:                                                parsed.getClassName(),
1427:                                                StringUtils
1428:                                                        .getDocumentPosition(
1429:                                                                content,
1430:                                                                term_start_index),
1431:                                                blockid,
1432:                                                config_term.mBlockappendTag
1433:                                                        .getText(),
1434:                                                config_term.mCommentTag
1435:                                                        .getText());
1436:                                    } else if (term_isblockvalue) {
1437:                                        throw new MismatchedTerminationTagException(
1438:                                                parsed.getClassName(),
1439:                                                StringUtils
1440:                                                        .getDocumentPosition(
1441:                                                                content,
1442:                                                                term_start_index),
1443:                                                blockid,
1444:                                                config_term.mBlockappendTag
1445:                                                        .getText(),
1446:                                                config_term.mBlockvalueTag
1447:                                                        .getText());
1448:                                    } else if (!term_isblockappend) {
1449:                                        throw new MismatchedTerminationTagException(
1450:                                                parsed.getClassName(),
1451:                                                StringUtils
1452:                                                        .getDocumentPosition(
1453:                                                                content,
1454:                                                                term_start_index),
1455:                                                blockid,
1456:                                                config_term.mBlockappendTag
1457:                                                        .getText(),
1458:                                                config_term.mBlockTag.getText());
1459:                                    }
1460:                                }
1461:                                // ensure that the block tags are closed with the appropriate termination tag
1462:                                else {
1463:                                    if (term_iscomment) {
1464:                                        throw new MismatchedTerminationTagException(
1465:                                                parsed.getClassName(),
1466:                                                StringUtils
1467:                                                        .getDocumentPosition(
1468:                                                                content,
1469:                                                                term_start_index),
1470:                                                blockid, config_term.mBlockTag
1471:                                                        .getText(),
1472:                                                config_term.mCommentTag
1473:                                                        .getText());
1474:                                    } else if (term_isblockvalue) {
1475:                                        throw new MismatchedTerminationTagException(
1476:                                                parsed.getClassName(),
1477:                                                StringUtils
1478:                                                        .getDocumentPosition(
1479:                                                                content,
1480:                                                                term_start_index),
1481:                                                blockid, config_term.mBlockTag
1482:                                                        .getText(),
1483:                                                config_term.mBlockvalueTag
1484:                                                        .getText());
1485:                                    } else if (term_isblockappend) {
1486:                                        throw new MismatchedTerminationTagException(
1487:                                                parsed.getClassName(),
1488:                                                StringUtils
1489:                                                        .getDocumentPosition(
1490:                                                                content,
1491:                                                                term_start_index),
1492:                                                blockid, config_term.mBlockTag
1493:                                                        .getText(),
1494:                                                config_term.mBlockappendTag
1495:                                                        .getText());
1496:                                    }
1497:                                }
1498:                            }
1499:                            // ensure that the comments tags are closed with the appropriate termination tag
1500:                            else if (!term_iscomment) {
1501:                                if (term_isblockvalue) {
1502:                                    throw new MismatchedTerminationTagException(
1503:                                            parsed.getClassName(), StringUtils
1504:                                                    .getDocumentPosition(
1505:                                                            content,
1506:                                                            term_start_index),
1507:                                            blockid, config_term.mCommentTag
1508:                                                    .getText(),
1509:                                            config_term.mBlockvalueTag
1510:                                                    .getText());
1511:                                } else if (term_isblockappend) {
1512:                                    throw new MismatchedTerminationTagException(
1513:                                            parsed.getClassName(), StringUtils
1514:                                                    .getDocumentPosition(
1515:                                                            content,
1516:                                                            term_start_index),
1517:                                            blockid, config_term.mCommentTag
1518:                                                    .getText(),
1519:                                            config_term.mBlockappendTag
1520:                                                    .getText());
1521:                                } else {
1522:                                    throw new MismatchedTerminationTagException(
1523:                                            parsed.getClassName(), StringUtils
1524:                                                    .getDocumentPosition(
1525:                                                            content,
1526:                                                            term_start_index),
1527:                                            blockid, config_term.mCommentTag
1528:                                                    .getText(),
1529:                                            config_term.mBlockTag.getText());
1530:                                }
1531:                            }
1532:
1533:                            // the block has been terminated, remove its id from the stack
1534:                            block_ids.pop();
1535:                            block_types.pop();
1536:                            block_configs.pop();
1537:
1538:                            // continue the search after this tag
1539:                            previous_index = term_end_index;
1540:                        }
1541:                    }
1542:                    // continue until no start and end tags are found anymore
1543:                    while (begin_start_index > -1 || term_start_index > -1);
1544:
1545:                    // check if all tags were correctly closed
1546:                    if (block_ids.size() > 1) {
1547:                        throw new MissingTerminationTagsException(parsed
1548:                                .getClassName(),
1549:                                StringUtils.getDocumentPosition(content,
1550:                                        content.length()), block_types.peek()
1551:                                        .getText());
1552:                    }
1553:                }
1554:
1555:                // append everything that's after the last block to the general content
1556:                blocks.get("").append(content.substring(previous_index));
1557:
1558:                // iterate over the supported parser configurations to find the ones
1559:                // that are unused for value tags
1560:                List<Parser.Config> value_configs_list = new ArrayList<Parser.Config>();
1561:                for (Parser.Config config : mConfigs) {
1562:                    if (getEscapedIndex(content, config.mTagStart.getText(), 0,
1563:                            config.mValueTag, config.mStringDelimiterBegin) != null
1564:                            || getEscapedIndex(content, config.mTagTerm
1565:                                    .getText(), 0, config.mValueTag,
1566:                                    config.mTagEnd) != null) {
1567:                        value_configs_list.add(config);
1568:                    }
1569:                }
1570:                Parser.Config[] value_configs = null;
1571:                if (value_configs_list.size() > 0) {
1572:                    // make a config array, containing only the used configs
1573:                    value_configs = new Parser.Config[value_configs_list.size()];
1574:                    value_configs_list.toArray(value_configs);
1575:                }
1576:
1577:                Parser.Config[] unescape_configs = getUnescapeConfigs(content);
1578:
1579:                // chop the blocks into block parts according to the value tags that are found in each block
1580:                for (String block_key : blocks.keySet()) {
1581:                    parsed.setBlock(block_key, parseBlockParts(value_configs,
1582:                            unescape_configs, parsed, blocks.get(block_key)
1583:                                    .toString()));
1584:                }
1585:            }
1586:
1587:            private ParsedBlockData parseBlockParts(Parser.Config[] configs,
1588:                    Parser.Config[] unescapeConfigs, Parsed parsed,
1589:                    String content) throws TemplateException {
1590:                assert parsed != null;
1591:                assert content != null;
1592:
1593:                ParsedBlockData block_data = new ParsedBlockData();
1594:
1595:                int previous_index = 0;
1596:                if (configs != null) {
1597:                    // setup the parser variables
1598:                    TagMatch match = null;
1599:
1600:                    int begin_start_index = 0;
1601:                    boolean begin_isquoted = false;
1602:                    int term_start_index = 0;
1603:                    int term_end_index = 0;
1604:                    int valueid_start_index = 0;
1605:                    int valueid_end_index = 0;
1606:                    int begin_term_index = 0;
1607:
1608:                    int[] begin_start_indices = new int[configs.length];
1609:                    boolean[] begin_isquoted_flags = new boolean[configs.length];
1610:                    int[] delimiter_end_indices = new int[configs.length];
1611:                    int[] term_start_indices = new int[configs.length];
1612:                    int[] term_end_indices = new int[configs.length];
1613:
1614:                    String valueid = null;
1615:                    String valuetag_start = null;
1616:
1617:                    Parser.Config config_begin = null;
1618:                    Parser.Config config_term = null;
1619:
1620:                    // extracts all the parts that make up a block
1621:                    do {
1622:                        // iterate over the supported parser configurations to find all
1623:                        // start tag beginnings
1624:                        for (int i = 0; i < configs.length; i++) {
1625:                            match = getEscapedIndex(content,
1626:                                    configs[i].mTagStart.getText(),
1627:                                    previous_index, configs[i].mValueTag,
1628:                                    configs[i].mStringDelimiterBegin);
1629:                            if (null == match) {
1630:                                begin_start_indices[i] = -1;
1631:                                begin_isquoted_flags[i] = false;
1632:                                delimiter_end_indices[i] = -1;
1633:                            } else {
1634:                                begin_start_indices[i] = match.getMatch(0);
1635:                                begin_isquoted_flags[i] = match
1636:                                        .didFixedPartMatch(1);
1637:                                delimiter_end_indices[i] = match.getMatch(2);
1638:                            }
1639:                        }
1640:
1641:                        // find the start position of the next value begin tags by comparing
1642:                        // which is the earliest start tag beginning
1643:                        config_begin = null;
1644:                        begin_start_index = -1;
1645:                        begin_isquoted = false;
1646:                        for (int i = 0; i < begin_start_indices.length; i++) {
1647:                            if (begin_start_indices[i] != -1
1648:                                    && (-1 == begin_start_index || begin_start_indices[i] < begin_start_index)) {
1649:                                begin_start_index = begin_start_indices[i];
1650:                                begin_isquoted = begin_isquoted_flags[i];
1651:                                valueid_start_index = delimiter_end_indices[i];
1652:                                config_begin = configs[i];
1653:                            }
1654:                        }
1655:
1656:                        // check if a begin tag was found
1657:                        if (-1 != begin_start_index) {
1658:                            // add the text up to the beginning of this value tag to the list of parts
1659:                            if (previous_index < begin_start_index) {
1660:                                String part = unescapePart(unescapeConfigs,
1661:                                        content.substring(previous_index,
1662:                                                begin_start_index));
1663:                                block_data.addPart(new ParsedBlockText(part));
1664:                            }
1665:
1666:                            // get the string delimiter that marks the end of the value id
1667:                            ConfigPartMatch delimiter_end_match = null;
1668:                            int valueid_end_offset = 0;
1669:                            if (begin_isquoted) {
1670:                                delimiter_end_match = getFirstFoundPartIndex(
1671:                                        content, valueid_start_index,
1672:                                        config_begin.mStringDelimiterEnd);
1673:                            }
1674:
1675:                            if (delimiter_end_match != null
1676:                                    && delimiter_end_match.mPart != null) {
1677:                                valueid_end_index = delimiter_end_match.mIndex;
1678:                                valueid_end_offset = delimiter_end_match.mPart
1679:                                        .length();
1680:                            }
1681:                            // ensure that a name that started out quoted is already terminated with a delimiter
1682:                            else if (begin_isquoted
1683:                                    && null == delimiter_end_match.mPart) {
1684:                                throw new AttributeNotEndedException(parsed
1685:                                        .getClassName(), StringUtils
1686:                                        .getDocumentPosition(content,
1687:                                                valueid_start_index),
1688:                                        config_begin.mValueTag.getText(),
1689:                                        "name");
1690:                            } else {
1691:                                int long_index = content.indexOf(
1692:                                        config_begin.mTagEnd.getText(),
1693:                                        valueid_start_index);
1694:                                int short_index = content.indexOf(
1695:                                        config_begin.mTagShortTerm.getText(),
1696:                                        valueid_start_index);
1697:                                int first_index = getFirstMatch(long_index,
1698:                                        short_index);
1699:                                valueid_end_offset = 0;
1700:                                valueid_end_index = backtrackTillFirstNonWhitespace(
1701:                                        content, first_index);
1702:                            }
1703:
1704:                            // check if the value id was ended
1705:                            if (-1 == valueid_end_index) {
1706:                                throw new AttributeNotEndedException(parsed
1707:                                        .getClassName(), StringUtils
1708:                                        .getDocumentPosition(content,
1709:                                                valueid_start_index),
1710:                                        config_begin.mValueTag.getText(),
1711:                                        "name");
1712:                            }
1713:
1714:                            // extract the appearance of the value tag start so that it can be reused later
1715:                            // when the value hasn't been set in the template
1716:                            valuetag_start = content.substring(
1717:                                    begin_start_index, valueid_start_index);
1718:
1719:                            // extract the id of the value and store it
1720:                            valueid = content.substring(valueid_start_index,
1721:                                    valueid_end_index);
1722:
1723:                            // ensure that an end delimiter corresponds to a start delimiter
1724:                            if (!begin_isquoted
1725:                                    && valueid
1726:                                            .endsWith(config_begin.mStringDelimiterEnd
1727:                                                    .getText())) {
1728:                                throw new AttributeWronglyEndedException(
1729:                                        parsed.getClassName(),
1730:                                        StringUtils
1731:                                                .getDocumentPosition(
1732:                                                        content,
1733:                                                        valueid_end_index
1734:                                                                + valueid_end_offset
1735:                                                                - config_begin.mStringDelimiterEnd
1736:                                                                        .length()),
1737:                                        config_begin.mValueTag.getText(),
1738:                                        "name");
1739:                            }
1740:
1741:                            // add the value ID to the list of parsed values
1742:                            parsed.addValue(valueid);
1743:
1744:                            // get the first tag ending
1745:                            begin_term_index = content.indexOf(
1746:                                    config_begin.mTagEnd.getText(),
1747:                                    valueid_end_index + valueid_end_offset);
1748:
1749:                            // ensure that the tag was propertly ended
1750:                            if (-1 == begin_term_index) {
1751:                                throw new BeginTagNotEndedException(parsed
1752:                                        .getClassName(), StringUtils
1753:                                        .getDocumentPosition(content,
1754:                                                valueid_end_index
1755:                                                        + valueid_end_offset),
1756:                                        config_begin.mValueTag.getText(),
1757:                                        valueid);
1758:                            }
1759:
1760:                            // check if it is short value tag
1761:                            int begin_short_term_index = begin_term_index
1762:                                    - (config_begin.mTagShortTermLength - config_begin.mTagEndLength);
1763:                            if (config_begin.mTagShortTerm
1764:                                    .equals(content
1765:                                            .substring(
1766:                                                    begin_short_term_index,
1767:                                                    begin_short_term_index
1768:                                                            + config_begin.mTagShortTermLength))) {
1769:                                // check that the begin tag termination is valid
1770:                                if (content.substring(
1771:                                        valueid_end_index + valueid_end_offset,
1772:                                        begin_short_term_index).trim().length() > 0) {
1773:                                    throw new BeginTagBadlyTerminatedException(
1774:                                            parsed.getClassName(),
1775:                                            StringUtils
1776:                                                    .getDocumentPosition(
1777:                                                            content,
1778:                                                            valueid_end_index
1779:                                                                    + valueid_end_offset),
1780:                                            config_begin.mValueTag.getText(),
1781:                                            valueid);
1782:                                }
1783:
1784:                                term_start_index = begin_short_term_index;
1785:
1786:                                // add the value part
1787:                                block_data
1788:                                        .addPart(new ParsedBlockValue(
1789:                                                valueid,
1790:                                                content
1791:                                                        .substring(
1792:                                                                begin_start_index,
1793:                                                                begin_short_term_index
1794:                                                                        + config_begin.mTagShortTermLength)));
1795:
1796:                                // continue the search after this tag
1797:                                previous_index = term_start_index
1798:                                        + config_begin.mTagShortTermLength;
1799:                            }
1800:                            // check if it was a long value tag
1801:                            else {
1802:                                // check that the begin tag termination is valid
1803:                                if (content.substring(
1804:                                        valueid_end_index + valueid_end_offset,
1805:                                        begin_term_index).trim().length() > 0) {
1806:                                    throw new BeginTagBadlyTerminatedException(
1807:                                            parsed.getClassName(),
1808:                                            StringUtils
1809:                                                    .getDocumentPosition(
1810:                                                            content,
1811:                                                            valueid_end_index
1812:                                                                    + valueid_end_offset),
1813:                                            config_begin.mValueTag.getText(),
1814:                                            valueid);
1815:                                }
1816:
1817:                                // iterate over the supported parser configurations all term tag
1818:                                // beginnings
1819:                                for (int i = 0; i < configs.length; i++) {
1820:                                    match = getEscapedIndex(content,
1821:                                            configs[i].mTagTerm.getText(),
1822:                                            previous_index,
1823:                                            configs[i].mValueTag,
1824:                                            configs[i].mTagEnd);
1825:                                    if (null == match) {
1826:                                        term_start_indices[i] = -1;
1827:                                        term_end_indices[i] = -1;
1828:                                    } else {
1829:                                        term_start_indices[i] = match
1830:                                                .getMatch(0);
1831:                                        term_end_indices[i] = match.getMatch(2);
1832:                                    }
1833:                                }
1834:
1835:                                // find the start position of the next value term tags by comparing
1836:                                // which is the earliest term tag beginning
1837:                                config_term = null;
1838:                                term_start_index = -1;
1839:                                for (int i = 0; i < term_start_indices.length; i++) {
1840:                                    if (term_start_indices[i] != -1
1841:                                            && (-1 == term_start_index || term_start_indices[i] < term_start_index)) {
1842:                                        term_start_index = term_start_indices[i];
1843:                                        term_end_index = term_end_indices[i];
1844:                                        config_term = configs[i];
1845:                                    }
1846:                                }
1847:
1848:                                // the termination tag always has to be found if a begin tag was found
1849:                                if (-1 == term_start_index) {
1850:                                    throw new TagNotTerminatedException(parsed
1851:                                            .getClassName(), StringUtils
1852:                                            .getDocumentPosition(content,
1853:                                                    begin_start_index),
1854:                                            config_begin.mValueTag.getText(),
1855:                                            valueid);
1856:                                } else {
1857:                                    // iterate over all configuration to check if none introduce a nested value tag
1858:                                    for (Config config_tmp : configs) {
1859:                                        // get the start of the next begin value tag to check for nested value tags
1860:                                        match = getEscapedIndex(
1861:                                                content,
1862:                                                config_tmp.mTagStart.getText(),
1863:                                                valueid_start_index,
1864:                                                config_tmp.mValueTag,
1865:                                                config_tmp.mStringDelimiterBegin);
1866:
1867:                                        // nested value tags are not permitted
1868:                                        if (match != null
1869:                                                && match.getMatch(0) < term_start_index) {
1870:                                            throw new UnsupportedNestedTagException(
1871:                                                    parsed.getClassName(),
1872:                                                    StringUtils
1873:                                                            .getDocumentPosition(
1874:                                                                    content,
1875:                                                                    match
1876:                                                                            .getMatch(0)),
1877:                                                    config_tmp.mValueTag
1878:                                                            .getText(), valueid);
1879:                                        }
1880:                                    }
1881:
1882:                                    // check if an unopened value tag isn't being closed
1883:                                    if (begin_term_index > term_start_index) {
1884:                                        throw new TerminatingUnopenedTagException(
1885:                                                parsed.getClassName(),
1886:                                                StringUtils
1887:                                                        .getDocumentPosition(
1888:                                                                content,
1889:                                                                term_start_index),
1890:                                                config_term.mValueTag.getText());
1891:                                    }
1892:
1893:                                    // the termination tag comes after the begin tag and the next begin tag after the termination tag
1894:                                    parsed
1895:                                            .setDefaultValue(
1896:                                                    valueid,
1897:                                                    unescapePart(
1898:                                                            unescapeConfigs,
1899:                                                            content
1900:                                                                    .substring(
1901:                                                                            begin_term_index
1902:                                                                                    + config_begin.mTagEndLength,
1903:                                                                            term_start_index)));
1904:
1905:                                    // add the value part
1906:                                    block_data
1907:                                            .addPart(new ParsedBlockValue(
1908:                                                    valueid,
1909:                                                    valuetag_start
1910:                                                            + valueid
1911:                                                            + config_term.mStringDelimiterEnd
1912:                                                            + config_term.mTagShortTerm));
1913:
1914:                                    // continue the search after this tag
1915:                                    previous_index = term_end_index;
1916:                                }
1917:                            }
1918:                        }
1919:                    }
1920:                    // continue until no start and end tags are found anymore
1921:                    while (begin_start_index > -1 && term_start_index > -1);
1922:                }
1923:
1924:                // append everything that's after the last value as a text block part
1925:                if (previous_index < content.length()) {
1926:                    block_data
1927:                            .addPart(new ParsedBlockText(unescapePart(
1928:                                    unescapeConfigs, content
1929:                                            .substring(previous_index))));
1930:                }
1931:
1932:                return block_data;
1933:            }
1934:
1935:            private FilteredTagsMap filterTags(Pattern[] filters,
1936:                    Collection<String> tags) {
1937:                FilteredTagsMap result = null;
1938:
1939:                if (filters != null) {
1940:                    result = new FilteredTagsMap();
1941:
1942:                    Matcher filter_matcher = null;
1943:                    ArrayList<String> captured_groups = null;
1944:                    String pattern = null;
1945:                    String[] captured_groups_array = null;
1946:
1947:                    ArrayList<String> filtered_tags = new ArrayList<String>();
1948:
1949:                    // iterate over the tag filters
1950:                    for (Pattern filter_pattern : filters) {
1951:                        // go over all the tags and try to match them against the current filter
1952:                        for (String tag : tags) {
1953:                            // skip over tags that have already been filtered
1954:                            if (filtered_tags.contains(tag)) {
1955:                                continue;
1956:                            }
1957:
1958:                            // create the filter matcher
1959:                            filter_matcher = filter_pattern.matcher(tag);
1960:
1961:                            // if the filter matches, and it returned capturing groups,
1962:                            // add the returned groups to the filtered tag mapping
1963:                            while (filter_matcher.find()) {
1964:                                if (null == captured_groups) {
1965:                                    captured_groups = new ArrayList<String>();
1966:                                    captured_groups.add(tag);
1967:                                }
1968:
1969:                                if (filter_matcher.groupCount() > 0) {
1970:                                    // store the captured groups
1971:                                    for (int j = 1; j <= filter_matcher
1972:                                            .groupCount(); j++) {
1973:                                        captured_groups.add(filter_matcher
1974:                                                .group(j));
1975:                                    }
1976:                                }
1977:                            }
1978:
1979:                            if (captured_groups != null) {
1980:                                pattern = filter_pattern.pattern();
1981:
1982:                                captured_groups_array = new String[captured_groups
1983:                                        .size()];
1984:                                captured_groups.toArray(captured_groups_array);
1985:
1986:                                result.addFilteredTag(pattern,
1987:                                        captured_groups_array);
1988:                                filtered_tags.add(tag);
1989:
1990:                                captured_groups_array = null;
1991:                                captured_groups = null;
1992:                            }
1993:                        }
1994:                    }
1995:                }
1996:
1997:                return result;
1998:            }
1999:
2000:            static class TagMatch {
2001:                private IntArrayList mMatches = null;
2002:                private IntArrayList mFixedPartsMatched = null;
2003:
2004:                void addMatch(int match) {
2005:                    if (null == mMatches) {
2006:                        mMatches = new IntArrayList();
2007:                    }
2008:
2009:                    mMatches.add(match);
2010:                }
2011:
2012:                int getMatch(int index) {
2013:                    if (null == mMatches || index >= mMatches.size()) {
2014:                        return -1;
2015:                    }
2016:
2017:                    return mMatches.get(index);
2018:                }
2019:
2020:                void addFixedPartMatched(boolean match) {
2021:                    if (null == mFixedPartsMatched) {
2022:                        mFixedPartsMatched = new IntArrayList();
2023:                    }
2024:
2025:                    mFixedPartsMatched.add(match ? 1 : 0);
2026:                }
2027:
2028:                boolean didFixedPartMatch(int index) {
2029:                    if (null == mFixedPartsMatched
2030:                            || index >= mFixedPartsMatched.size()) {
2031:                        return false;
2032:                    }
2033:
2034:                    return 1 == mFixedPartsMatched.get(index);
2035:                }
2036:
2037:                void clear() {
2038:                    if (mMatches != null) {
2039:                        mMatches.clear();
2040:                    }
2041:
2042:                    if (mFixedPartsMatched != null) {
2043:                        mFixedPartsMatched.clear();
2044:                    }
2045:                }
2046:            }
2047:
2048:            static class ConfigPartMatch {
2049:                static final ConfigPartMatch NO_MATCH = new ConfigPartMatch(-1,
2050:                        null);
2051:
2052:                private int mIndex = -1;
2053:                private ConfigPart mPart = null;
2054:
2055:                ConfigPartMatch(int index, ConfigPart part) {
2056:                    mIndex = index;
2057:                    mPart = part;
2058:                }
2059:
2060:                int length() {
2061:                    if (null == mPart) {
2062:                        return 0;
2063:                    }
2064:
2065:                    return mPart.length();
2066:                }
2067:            }
2068:
2069:            public static class MandatoryConfigPart extends ConfigPart {
2070:                public MandatoryConfigPart(PartType type, String text) {
2071:                    super (type, text, false);
2072:                }
2073:            }
2074:
2075:            public static class OptionalConfigPart extends ConfigPart {
2076:                public OptionalConfigPart(PartType type, String text) {
2077:                    super (type, text, true);
2078:                }
2079:            }
2080:
2081:            public static class ConfigPart implements  CharSequence {
2082:                private PartType mType;
2083:                private String mText;
2084:                private boolean mOptional;
2085:
2086:                private ConfigPart(PartType type, String text, boolean optional) {
2087:                    mType = type;
2088:
2089:                    if (null == text) {
2090:                        text = "";
2091:                    }
2092:
2093:                    mText = text;
2094:                    mOptional = optional;
2095:                }
2096:
2097:                public PartType getType() {
2098:                    return mType;
2099:                }
2100:
2101:                public String getText() {
2102:                    return mText;
2103:                }
2104:
2105:                public boolean isOptional() {
2106:                    return mOptional;
2107:                }
2108:
2109:                public int length() {
2110:                    return mText.length();
2111:                }
2112:
2113:                public char charAt(int index) {
2114:                    return mText.charAt(index);
2115:                }
2116:
2117:                public CharSequence subSequence(int start, int end) {
2118:                    return mText.subSequence(start, end);
2119:                }
2120:
2121:                public String toString() {
2122:                    return mText;
2123:                }
2124:
2125:                public boolean equals(Object other) {
2126:                    if (null == other) {
2127:                        return false;
2128:                    }
2129:
2130:                    if (other == this ) {
2131:                        return true;
2132:                    }
2133:
2134:                    if (other instanceof  CharSequence) {
2135:                        return mText.equals(other);
2136:                    }
2137:
2138:                    if (!(other instanceof  ConfigPart)) {
2139:                        return false;
2140:                    }
2141:
2142:                    ConfigPart other_part = (ConfigPart) other;
2143:                    return mType == other_part.mType
2144:                            && mOptional == other_part.mOptional
2145:                            && mText.equals(other_part.mText);
2146:                }
2147:
2148:                public int hashCode() {
2149:                    return mType.hashCode() * mText.hashCode()
2150:                            * (mOptional ? 1 : 0);
2151:                }
2152:            }
2153:
2154:            static class Config implements  Cloneable {
2155:                private ConfigPart mTagStart = null;
2156:                private ConfigPart mTagEnd = null;
2157:                private ConfigPart mTagTerm = null;
2158:                private ConfigPart mTagShortTerm = null;
2159:                private ConfigPart mStringDelimiterBegin = null;
2160:                private ConfigPart mStringDelimiterEnd = null;
2161:                private ConfigPart mValueTag = null;
2162:                private ConfigPart mBlockTag = null;
2163:                private ConfigPart mBlockvalueTag = null;
2164:                private ConfigPart mBlockappendTag = null;
2165:                private ConfigPart mIncludeTag = null;
2166:                private ConfigPart mCommentTag = null;
2167:                private ConfigPart mUnescapeStart = null;
2168:
2169:                private Pattern mUnescapePattern = null;
2170:
2171:                private int mTagEndLength = -1;
2172:                private int mTagTermLength = -1;
2173:                private int mTagShortTermLength = -1;
2174:
2175:                Config(String tagStart, String tagEnd, String tagTerm,
2176:                        String tagShortTerm, ConfigPart stringDelimiterBegin,
2177:                        ConfigPart stringDelimiterEnd, String valueTag,
2178:                        String blockTag, String blockvalueTag,
2179:                        String blockappendTag, String includeTag,
2180:                        String commentTag) {
2181:                    assert tagStart != null;
2182:                    assert tagEnd != null;
2183:                    assert stringDelimiterBegin != null;
2184:                    assert stringDelimiterEnd != null;
2185:                    assert tagTerm != null;
2186:                    assert tagShortTerm != null;
2187:                    assert valueTag != null;
2188:                    assert blockTag != null;
2189:                    assert blockvalueTag != null;
2190:                    assert blockappendTag != null;
2191:                    assert includeTag != null;
2192:                    assert commentTag != null;
2193:
2194:                    mTagStart = new MandatoryConfigPart(PartType.TAG_START,
2195:                            tagStart);
2196:                    mTagEnd = new MandatoryConfigPart(PartType.TAG_END, tagEnd);
2197:                    mStringDelimiterBegin = stringDelimiterBegin;
2198:                    mStringDelimiterEnd = stringDelimiterEnd;
2199:                    mValueTag = new MandatoryConfigPart(PartType.VALUE,
2200:                            valueTag);
2201:                    mBlockTag = new MandatoryConfigPart(PartType.BLOCK,
2202:                            blockTag);
2203:                    mBlockvalueTag = new MandatoryConfigPart(
2204:                            PartType.BLOCKVALUE, blockvalueTag);
2205:                    mBlockappendTag = new MandatoryConfigPart(
2206:                            PartType.BLOCKAPPEND, blockappendTag);
2207:                    mIncludeTag = new MandatoryConfigPart(PartType.INCLUDE,
2208:                            includeTag);
2209:                    mCommentTag = new MandatoryConfigPart(PartType.COMMENT,
2210:                            commentTag);
2211:                    mTagTerm = new MandatoryConfigPart(PartType.TAG_TERM,
2212:                            tagTerm);
2213:                    mTagShortTerm = new MandatoryConfigPart(
2214:                            PartType.TAG_SHORT_TERM, tagShortTerm);
2215:                    mUnescapeStart = new MandatoryConfigPart(
2216:                            PartType.UNESCAPE_START, "\\" + tagStart);
2217:                    mUnescapePattern = Pattern.compile("\\\\((?:\\Q" + tagStart
2218:                            + "\\E|\\Q" + mTagTerm + "\\E)\\s*(?:"
2219:                            + mIncludeTag + "|" + mBlockTag + "|"
2220:                            + mBlockvalueTag + "|" + mBlockappendTag + "|"
2221:                            + mValueTag + "|" + mCommentTag + "))");
2222:
2223:                    mTagEndLength = mTagEnd.length();
2224:                    mTagTermLength = mTagTerm.length();
2225:                    mTagShortTermLength = mTagShortTerm.length();
2226:
2227:                    assert mTagTerm != null;
2228:                    assert mTagShortTerm != null;
2229:                    assert mStringDelimiterBegin != null;
2230:                    assert mStringDelimiterEnd != null;
2231:                    assert mTagEndLength > 0;
2232:                    assert mTagTermLength > 0;
2233:                    assert mTagShortTermLength > 0;
2234:                }
2235:
2236:                public Config clone() {
2237:                    Config new_parserconfig = null;
2238:                    try {
2239:                        new_parserconfig = (Config) super .clone();
2240:                    } catch (CloneNotSupportedException e) {
2241:                        new_parserconfig = null;
2242:                    }
2243:
2244:                    return new_parserconfig;
2245:                }
2246:
2247:                public boolean equals(Object object) {
2248:                    if (object == this ) {
2249:                        return true;
2250:                    }
2251:
2252:                    if (null == object) {
2253:                        return false;
2254:                    }
2255:
2256:                    if (!(object instanceof  Config)) {
2257:                        return false;
2258:                    }
2259:
2260:                    Config other_parserconfig = (Config) object;
2261:                    if (other_parserconfig.mTagStart.equals(this .mTagStart)
2262:                            && other_parserconfig.mTagEnd.equals(this .mTagEnd)
2263:                            && other_parserconfig.mTagTerm
2264:                                    .equals(this .mTagTerm)
2265:                            && other_parserconfig.mTagShortTerm
2266:                                    .equals(this .mTagShortTerm)
2267:                            && other_parserconfig.mStringDelimiterBegin
2268:                                    .equals(this .mStringDelimiterBegin)
2269:                            && other_parserconfig.mStringDelimiterEnd
2270:                                    .equals(this .mStringDelimiterEnd)
2271:                            && other_parserconfig.mValueTag
2272:                                    .equals(this .mValueTag)
2273:                            && other_parserconfig.mBlockTag
2274:                                    .equals(this .mBlockTag)
2275:                            && other_parserconfig.mBlockvalueTag
2276:                                    .equals(this .mBlockvalueTag)
2277:                            && other_parserconfig.mBlockappendTag
2278:                                    .equals(this .mBlockappendTag)
2279:                            && other_parserconfig.mIncludeTag
2280:                                    .equals(this .mIncludeTag)
2281:                            && other_parserconfig.mCommentTag
2282:                                    .equals(this .mCommentTag)) {
2283:                        return true;
2284:                    } else {
2285:                        return false;
2286:                    }
2287:                }
2288:            }
2289:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.