├── .gitignore ├── NOTICE.txt ├── src ├── test │ ├── resources │ │ ├── log4j.properties │ │ └── test1.html │ └── java │ │ └── org │ │ └── thymeleaf │ │ └── extras │ │ └── conditionalcomments │ │ ├── dialect │ │ └── ConditionalCommentsDialectTest.java │ │ └── util │ │ └── ConditionalCommentUtilsTest.java ├── assembly │ └── dist.xml └── main │ └── java │ └── org │ └── thymeleaf │ └── extras │ └── conditionalcomments │ ├── dialect │ ├── ConditionalCommentsDialect.java │ └── processor │ │ ├── ConditionalCommentNodeProcessorMatcher.java │ │ └── ConditionalCommentProcessor.java │ ├── util │ ├── ConditionalCommentParsingResult.java │ └── ConditionalCommentUtils.java │ └── parser │ └── ConditionalCommentAttoTemplateParser.java ├── .gitattributes ├── BUILD.txt ├── README.markdown ├── LICENSE.txt ├── pom.xml └── CONTRIBUTING.markdown /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | target/ 4 | bin/ 5 | .settings/ 6 | .idea/ 7 | *.iml 8 | 9 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger = WARN, stdout 2 | 3 | log4j.appender.stdout = org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 5 | log4j.appender.stdout.layout.ConversionPattern=%d %5p [%c{1}] (%F:%L) - %m%n 6 | 7 | log4j.logger.org.thymeleaf=TRACE 8 | log4j.logger.org.thymeleaf.TemplateEngine.CONFIG=DEBUG 9 | log4j.logger.org.thymeleaf.TemplateEngine.TIMER=DEBUG 10 | log4j.logger.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=DEBUG 11 | log4j.logger.org.thymeleaf.TemplateEngine.cache.FRAGMENT_CACHE=DEBUG 12 | log4j.logger.org.thymeleaf.TemplateEngine.cache.MESSAGE_CACHE=DEBUG 13 | log4j.logger.org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE=DEBUG 14 | -------------------------------------------------------------------------------- /src/test/resources/test1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | Testing conditional comments 8 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 |

21 | This works! 22 |

23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | *.java text diff=java 5 | *.properties text 6 | *.js text 7 | *.css text 8 | *.less text 9 | *.html text diff=html 10 | *.jsp text diff=html 11 | *.jspx text diff=html 12 | *.tag text diff=html 13 | *.tagx text diff=html 14 | *.tld text 15 | *.xml text 16 | *.gradle text 17 | 18 | *.sql text 19 | 20 | *.xsd text 21 | *.dtd text 22 | *.mod text 23 | *.ent text 24 | 25 | *.txt text 26 | *.md text 27 | *.markdown text 28 | 29 | *.thtest text 30 | *.thindex text 31 | *.common text 32 | 33 | *.odt binary 34 | *.pdf binary 35 | 36 | *.sh text eol=lf 37 | *.bat text eol=crlf 38 | 39 | *.ico binary 40 | *.png binary 41 | *.svg binary 42 | *.woff binary 43 | 44 | *.rar binary 45 | *.zargo binary 46 | *.zip binary 47 | 48 | CNAME text 49 | *.MF text 50 | -------------------------------------------------------------------------------- /BUILD.txt: -------------------------------------------------------------------------------- 1 | 2 | Building thymeleaf-extras-conditionalcomments 3 | --------------------------------------------- 4 | 5 | To build thymeleaf-extras-conditionalcomments you will need Maven 2. You can 6 | get it at: 7 | 8 | http://maven.apache.org 9 | 10 | Build and install the project executing, from the project's root folder: 11 | 12 | mvn clean:clean install 13 | 14 | And you will get a fresh target/thymeleaf-extras-conditionalcomments-{version}.jar 15 | file. You will also get it installed in your local repository at: 16 | 17 | $M2_REPO/org/thymeleaf/extras/thymeleaf-extras-conditionalcomments/{version}/thymeleaf-extras-conditionalcomments-{version}.jar 18 | 19 | 20 | 21 | Generating Javadoc for thymeleaf-extras-conditionalcomments 22 | ----------------------------------------------------------- 23 | 24 | If you wish to generate the javadoc for thymeleaf-extras-conditionalcomments, 25 | execute this from the project root folder: 26 | 27 | mvn javadoc:javadoc 28 | 29 | This will generate the javadoc documentation in HTML format in: 30 | 31 | target/site/apidocs 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | 2 | Thymeleaf - Module for Internet Explorer conditional comments 3 | ============================================================= 4 | 5 | ------------------------------------------------------------------------------ 6 | 7 | Status 8 | ------ 9 | 10 | This is a *thymeleaf extras* module, not a part of the Thymeleaf core (and as 11 | such following its own versioning schema), but fully supported by the 12 | Thymeleaf team. 13 | 14 | Current versions: 15 | 16 | * **Version 2.1.2.RELEASE** - for Thymeleaf 2.1 (requires 2.1.3+) 17 | * **Version 2.0.0** - for Thymeleaf 2.0 (requires 2.0.15+) 18 | 19 | 20 | License 21 | ------- 22 | 23 | This software is licensed under the [Apache License 2.0] 24 | (http://www.apache.org/licenses/LICENSE-2.0.html). 25 | 26 | 27 | Requirements 28 | ------------ 29 | 30 | * Thymeleaf **2.1.3+** 31 | * Attoparser **2.0.0+** [http://www.attoparser.org] 32 | 33 | 34 | Maven info 35 | ---------- 36 | 37 | * groupId: `org.thymeleaf.extras` 38 | * artifactId: `thymeleaf-extras-conditionalcomments` 39 | 40 | 41 | Distribution packages 42 | --------------------- 43 | 44 | Distribution packages (binaries + sources + javadoc) can be downloaded from [SourceForge](http://sourceforge.net/projects/thymeleaf/files/thymeleaf-extras-conditionalcomments/). 45 | 46 | 47 | Installation 48 | ------------ 49 | 50 | Just add the `org.thymeleaf.extras.conditionalcomments.dialect.ConditionalCommentsDialect` 51 | class to the list of dialects in your TemplateEngine implementation, and conditional comment 52 | processing will work out-of-the-box. 53 | 54 | 55 | Features 56 | -------- 57 | 58 | This module allows you to correctly process Internet Explorer Conditional 59 | comments inside Thymeleaf templates. 60 | -------------------------------------------------------------------------------- /src/assembly/dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | dist 7 | 8 | 9 | zip 10 | 11 | 12 | 13 | 14 | ${basedir}/target/apidocs/ 15 | /apidocs/thymeleaf-extras-conditionalcomments/ 16 | 17 | **/* 18 | 19 | 20 | 21 | ${basedir}/target/ 22 | /dist 23 | 24 | thymeleaf-extras-conditionalcomments-${project.version}.jar 25 | thymeleaf-extras-conditionalcomments-${project.version}-javadoc.jar 26 | thymeleaf-extras-conditionalcomments-${project.version}-sources.jar 27 | 28 | 29 | 30 | ${basedir}/ 31 | / 32 | 33 | LICENSE.txt 34 | README.markdown 35 | NOTICE.txt 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | /lib 44 | false 45 | runtime 46 | 47 | org.thymeleaf.extras:thymeleaf-extras-conditionalcomments 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/conditionalcomments/dialect/ConditionalCommentsDialect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.conditionalcomments.dialect; 21 | 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | import org.thymeleaf.dialect.AbstractDialect; 26 | import org.thymeleaf.extras.conditionalcomments.dialect.processor.ConditionalCommentProcessor; 27 | import org.thymeleaf.processor.IProcessor; 28 | import org.thymeleaf.processor.document.ProcessCommentNodesDocumentProcessor; 29 | 30 | 31 | 32 | /** 33 | * 34 | * @author Daniel Fernández 35 | * 36 | */ 37 | public class ConditionalCommentsDialect extends AbstractDialect { 38 | 39 | 40 | public ConditionalCommentsDialect() { 41 | super(); 42 | } 43 | 44 | 45 | 46 | public String getPrefix() { 47 | // No attribute or tag processors, so we don't need a prefix at all and 48 | // we can return whichever value. 49 | return "condcom"; 50 | } 51 | 52 | 53 | public boolean isLenient() { 54 | return false; 55 | } 56 | 57 | 58 | 59 | 60 | @Override 61 | public Set getProcessors() { 62 | final Set processors = new HashSet(); 63 | processors.add(new ConditionalCommentProcessor()); 64 | // This processor is needed to enable the processing of Comment nodes 65 | processors.add(new ProcessCommentNodesDocumentProcessor()); 66 | return processors; 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/org/thymeleaf/extras/conditionalcomments/dialect/ConditionalCommentsDialectTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.conditionalcomments.dialect; 21 | 22 | import java.io.StringWriter; 23 | import java.io.Writer; 24 | 25 | import junit.framework.TestCase; 26 | 27 | import org.thymeleaf.TemplateEngine; 28 | import org.thymeleaf.context.Context; 29 | import org.thymeleaf.context.IContext; 30 | import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; 31 | 32 | 33 | 34 | 35 | 36 | public final class ConditionalCommentsDialectTest extends TestCase { 37 | 38 | 39 | private static final IContext EMPTY_CONTEXT = new Context(); 40 | 41 | 42 | 43 | public void testDialect() throws Exception { 44 | 45 | final TemplateEngine engine = initTemplateEngine(); 46 | 47 | { 48 | final Writer writer = new StringWriter(); 49 | 50 | engine.process("test1.html", EMPTY_CONTEXT, writer); 51 | System.out.println("\n\n********\n" + writer.toString() + "\n\n********\n\n"); 52 | 53 | } 54 | 55 | } 56 | 57 | 58 | 59 | 60 | 61 | private static TemplateEngine initTemplateEngine() throws Exception { 62 | 63 | final TemplateEngine engine = new TemplateEngine(); 64 | 65 | final ConditionalCommentsDialect conditionalCommentsDialect = new ConditionalCommentsDialect(); 66 | engine.addDialect(conditionalCommentsDialect); 67 | 68 | final ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver(); 69 | classLoaderTemplateResolver.setTemplateMode("HTML5"); 70 | engine.setTemplateResolver(classLoaderTemplateResolver); 71 | 72 | return engine; 73 | 74 | } 75 | 76 | 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/conditionalcomments/dialect/processor/ConditionalCommentNodeProcessorMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.conditionalcomments.dialect.processor; 21 | 22 | import org.thymeleaf.dom.Comment; 23 | import org.thymeleaf.dom.Node; 24 | import org.thymeleaf.extras.conditionalcomments.util.ConditionalCommentUtils; 25 | import org.thymeleaf.processor.ICommentNodeProcessorMatcher; 26 | import org.thymeleaf.processor.ProcessorMatchingContext; 27 | 28 | 29 | 30 | 31 | 32 | /** 33 | *

34 | * Implementation of {@link ICommentNodeProcessorMatcher} matching Comment nodes that 35 | * look like conditional comments. 36 | *

37 | *

38 | * In order to determine whether a comment is a conditional comment or not, 39 | * {@link ConditionalCommentUtils#isConditionalComment(String)} is used. 40 | *

41 | * 42 | * @author Daniel Fernández 43 | * 44 | * @since 1.0 45 | * 46 | */ 47 | public final class ConditionalCommentNodeProcessorMatcher implements ICommentNodeProcessorMatcher { 48 | 49 | 50 | public static ConditionalCommentNodeProcessorMatcher INSTANCE = new ConditionalCommentNodeProcessorMatcher(); 51 | 52 | 53 | /** 54 | *

55 | * Create a new matcher of this class. 56 | *

57 | */ 58 | public ConditionalCommentNodeProcessorMatcher() { 59 | super(); 60 | } 61 | 62 | 63 | 64 | /** 65 | *

66 | * Matches the specified {@link Node} if it is an instance of {@link Comment} 67 | * AND it conforms to {@link ConditionalCommentUtils#isConditionalComment(String)}. 68 | *

69 | * 70 | * @param node the node to be checked 71 | * @param context the processor matching context 72 | * @return true if node is a conditional comment, false if not 73 | */ 74 | public boolean matches(final Node node, final ProcessorMatchingContext context) { 75 | 76 | if (node == null || !(node instanceof Comment)) { 77 | // fail fast 78 | return false; 79 | } 80 | 81 | final Comment comment = (Comment) node; 82 | return ConditionalCommentUtils.isConditionalComment(comment.getContent()); 83 | 84 | } 85 | 86 | 87 | 88 | /** 89 | *

90 | * Applies to {@link Comment} nodes. 91 | *

92 | */ 93 | public final Class appliesTo() { 94 | return Comment.class; 95 | } 96 | 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/conditionalcomments/util/ConditionalCommentParsingResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.conditionalcomments.util; 21 | 22 | 23 | 24 | 25 | 26 | 27 | /** 28 | *

29 | * Specification of the diverse fragments of a conditional comment, 30 | * once parsed. 31 | *

32 | *

33 | * All fragments are specified as (offset, len) pairs 34 | * on the original buffer (text). In order to build String 35 | * objects for these fragments, only new String(text, offset, len) 36 | * is required. 37 | *

38 | *

39 | * Format is: 40 | *

41 | *

 42 |  *    <!--[start-condition]>content<![end-condition]-->
 43 |  * 
44 | * 45 | * @author Daniel Fernández 46 | * 47 | * @since 1.0 48 | * 49 | */ 50 | public final class ConditionalCommentParsingResult { 51 | 52 | 53 | private final String text; 54 | private final int startExpressionOffset; 55 | private final int startExpressionLen; 56 | private final int contentOffset; 57 | private final int contentLen; 58 | private final int endExpressionOffset; 59 | private final int endExpressionLen; 60 | 61 | 62 | 63 | 64 | public ConditionalCommentParsingResult(final String text, 65 | final int startExpressionOffset, final int startExpressionLen, 66 | final int contentOffset, final int contentLen, final int endExpressionOffset, 67 | final int endExpressionLen) { 68 | 69 | super(); 70 | 71 | this.text = text; 72 | this.startExpressionOffset = startExpressionOffset; 73 | this.startExpressionLen = startExpressionLen; 74 | this.contentOffset = contentOffset; 75 | this.contentLen = contentLen; 76 | this.endExpressionOffset = endExpressionOffset; 77 | this.endExpressionLen = endExpressionLen; 78 | 79 | } 80 | 81 | 82 | 83 | 84 | public String getText() { 85 | return this.text; 86 | } 87 | 88 | public int getStartExpressionOffset() { 89 | return this.startExpressionOffset; 90 | } 91 | 92 | public int getStartExpressionLen() { 93 | return this.startExpressionLen; 94 | } 95 | 96 | public int getContentOffset() { 97 | return this.contentOffset; 98 | } 99 | 100 | public int getContentLen() { 101 | return this.contentLen; 102 | } 103 | 104 | public int getEndExpressionOffset() { 105 | return this.endExpressionOffset; 106 | } 107 | 108 | public int getEndExpressionLen() { 109 | return this.endExpressionLen; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/conditionalcomments/dialect/processor/ConditionalCommentProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.conditionalcomments.dialect.processor; 21 | 22 | import java.io.IOException; 23 | import java.io.StringWriter; 24 | import java.util.List; 25 | 26 | import org.thymeleaf.Arguments; 27 | import org.thymeleaf.dom.Comment; 28 | import org.thymeleaf.dom.Document; 29 | import org.thymeleaf.dom.Node; 30 | import org.thymeleaf.exceptions.TemplateProcessingException; 31 | import org.thymeleaf.extras.conditionalcomments.parser.ConditionalCommentAttoTemplateParser; 32 | import org.thymeleaf.extras.conditionalcomments.util.ConditionalCommentParsingResult; 33 | import org.thymeleaf.extras.conditionalcomments.util.ConditionalCommentUtils; 34 | import org.thymeleaf.processor.ProcessorResult; 35 | import org.thymeleaf.processor.comment.AbstractCommentNodeProcessor; 36 | import org.thymeleaf.templatemode.ITemplateModeHandler; 37 | import org.thymeleaf.templatewriter.ITemplateWriter; 38 | 39 | 40 | 41 | /** 42 | * 43 | * @author Daniel Fernández 44 | * 45 | * @since 1.0 46 | * 47 | */ 48 | public class ConditionalCommentProcessor extends AbstractCommentNodeProcessor { 49 | 50 | 51 | public static final int PRECEDENCE = 1000; 52 | 53 | 54 | 55 | public ConditionalCommentProcessor() { 56 | super(ConditionalCommentNodeProcessorMatcher.INSTANCE); 57 | } 58 | 59 | 60 | 61 | @Override 62 | public int getPrecedence() { 63 | return PRECEDENCE; 64 | } 65 | 66 | 67 | 68 | @Override 69 | protected ProcessorResult processCommentNode(final Arguments arguments, final Comment commentNode) { 70 | 71 | final ConditionalCommentParsingResult parsingResult = 72 | ConditionalCommentUtils.parseConditionalComment(commentNode.getContent()); 73 | 74 | 75 | final StringWriter writer = new StringWriter(); 76 | 77 | /* 78 | * Rebuild the conditional comment start expression 79 | */ 80 | writer.write("["); 81 | writer.write(parsingResult.getText(), parsingResult.getStartExpressionOffset(), parsingResult.getStartExpressionLen()); 82 | writer.write("]>"); 83 | 84 | final ConditionalCommentAttoTemplateParser parser = new ConditionalCommentAttoTemplateParser(); 85 | 86 | final List nodes = 87 | parser.parseFragment( 88 | arguments.getConfiguration(), 89 | parsingResult.getText(), 90 | parsingResult.getContentOffset(), 91 | parsingResult.getContentLen()); 92 | 93 | final String templateMode = 94 | arguments.getTemplateResolution().getTemplateMode(); 95 | final ITemplateModeHandler templateModeHandler = 96 | arguments.getConfiguration().getTemplateModeHandler(templateMode); 97 | final ITemplateWriter templateWriter = templateModeHandler.getTemplateWriter(); 98 | 99 | 100 | final Document document = 101 | new Document("[Conditional Comment at line: " + commentNode.getLineNumber() + "]"); 102 | document.setChildren(nodes); 103 | 104 | document.process(arguments); 105 | 106 | try { 107 | templateWriter.write(arguments, writer, document); 108 | } catch (final IOException e) { 109 | throw new TemplateProcessingException( 110 | "Error writing result of processing conditional comment at line " + 111 | commentNode.getLineNumber(), e); 112 | } 113 | 114 | 115 | /* 116 | * Rebuild the conditional comment end expression 117 | */ 118 | writer.write(" 29 | * Utility class for processing IE conditional comments. 30 | *

31 | *

32 | * Expected format is: 33 | *

34 | *

 35 |  *    <!--[start-condition]>content<![end-condition]-->
 36 |  * 
37 | * 38 | * @author Daniel Fernández 39 | * 40 | * @since 1.0 41 | * 42 | */ 43 | public final class ConditionalCommentUtils { 44 | 45 | 46 | /** 47 | *

48 | * Determines whether the text passed as argument is a conditional comment 49 | * or not. 50 | *

51 | *

52 | * This method makes use of {@link #parseConditionalComment(String)}. 53 | *

54 | * 55 | * @param text the text to be checked. 56 | * @return true if the text has the format of a conditional comment, false if not. 57 | */ 58 | public static boolean isConditionalComment(final String text) { 59 | return parseConditionalComment(text) != null; 60 | } 61 | 62 | 63 | 64 | /** 65 | *

66 | * Tries to parse the text passed as argument as a conditional comment. 67 | *

68 | *

69 | * Result is an object of class {@link ConditionalCommentParsingResult}, or 70 | * null if text does not have the expected format for a conditional comment. 71 | *

72 | * 73 | * @param text the text to be parsed 74 | * @return a {@link ConditionalCommentParsingResult} object if text could be parsed, 75 | * null if format is invalid. 76 | */ 77 | public static ConditionalCommentParsingResult parseConditionalComment(final String text) { 78 | 79 | final int len = text.length(); 80 | int i = 0; 81 | 82 | 83 | // discard all initial whitespace 84 | while (i < len && Character.isWhitespace(text.charAt(i))) { i++; } 85 | 86 | // check the first char after whitespace is '[' 87 | if (i >= len || text.charAt(i++) != '[') { 88 | return null; 89 | } 90 | 91 | final int startExpressionOffset = i; 92 | 93 | // look for last position of start expression 94 | while (i < len && text.charAt(i) != ']') { i++; } 95 | 96 | if (i >= len) { 97 | return null; 98 | } 99 | 100 | final int startExpressionLen = (i - startExpressionOffset); 101 | 102 | // discard the ']' position 103 | i++; 104 | 105 | // discard all following whitespace 106 | while (i < len && Character.isWhitespace(text.charAt(i))) { i++; } 107 | 108 | // check the first non-whitespace char after ']' is '>' 109 | if (i >= len || text.charAt(i++) != '>') { 110 | return null; 111 | } 112 | 113 | final int contentOffset = i; 114 | 115 | // Once we've obtained all we needed from the start of the comment, switch place 116 | // and start looking for structures from the end. 117 | i = len - 1; 118 | 119 | // discard all final whitespace 120 | while (i > contentOffset && Character.isWhitespace(text.charAt(i))) { i--; } 121 | 122 | // check the first char after whitespace is ']' 123 | if (i <= contentOffset || text.charAt(i--) != ']') { 124 | return null; 125 | } 126 | 127 | final int endExpressionLastPos = i + 1; 128 | 129 | // look for first char of end expression 130 | while (i > contentOffset && text.charAt(i) != '[') { i--; } 131 | 132 | if (i <= contentOffset) { 133 | return null; 134 | } 135 | 136 | final int endExpressionOffset = i + 1; 137 | final int endExpressionLen = endExpressionLastPos - endExpressionOffset; 138 | 139 | // discard the '[' sign we have just processed 140 | i--; 141 | 142 | // discard all following whitespace 143 | while (i >= contentOffset && Character.isWhitespace(text.charAt(i))) { i--; } 144 | 145 | // check the first non-whitespace char before '[' is '!' 146 | if (i <= contentOffset || text.charAt(i--) != '!') { 147 | return null; 148 | } 149 | 150 | // check the first char before '!' is '<' 151 | if (i <= contentOffset || text.charAt(i--) != '<') { 152 | return null; 153 | } 154 | 155 | final int contentLen = (i + 1) - contentOffset; 156 | 157 | if (contentLen <= 0 || startExpressionLen <= 0 || endExpressionLen <= 0) { 158 | return null; 159 | } 160 | 161 | return new ConditionalCommentParsingResult( 162 | text, 163 | startExpressionOffset, startExpressionLen, 164 | contentOffset, contentLen, 165 | endExpressionOffset, endExpressionLen); 166 | 167 | } 168 | 169 | 170 | 171 | 172 | 173 | private ConditionalCommentUtils() { 174 | super(); 175 | } 176 | 177 | 178 | } 179 | -------------------------------------------------------------------------------- /src/test/java/org/thymeleaf/extras/conditionalcomments/util/ConditionalCommentUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.conditionalcomments.util; 21 | 22 | import junit.framework.TestCase; 23 | 24 | 25 | 26 | 27 | 28 | public final class ConditionalCommentUtilsTest extends TestCase { 29 | 30 | 31 | /* 32 | * FORMAT: 33 | * 34 | * 35 | */ 36 | 37 | public void testIsConditionalComment() throws Exception { 38 | 39 | { 40 | final String text = 41 | "[if lt IE 8]>\n" + 42 | " \n" + 43 | "\n"; 46 | final String ee = "endif"; 47 | 48 | 49 | checkParsing(text, se, c, ee); 50 | } 51 | 52 | { 53 | final String text = 54 | "[]>\n" + 55 | " \n" + 56 | "\n"; 59 | final String ee = "endif"; 60 | 61 | 62 | checkParsing(text, se, c, ee, true); 63 | } 64 | 65 | { 66 | final String text = 67 | "[if lt IE 8]>"; 70 | final String ee = "endif"; 71 | 72 | 73 | checkParsing(text, se, c, ee); 74 | } 75 | 76 | 77 | { 78 | final String text = 79 | "[if lt IE 8]"; 82 | final String ee = "endif"; 83 | 84 | 85 | checkParsing(text, se, c, ee, true); 86 | } 87 | 88 | 89 | { 90 | final String text = 91 | "[if lt IE 8>"; 94 | final String ee = "endif"; 95 | 96 | 97 | checkParsing(text, se, c, ee, true); 98 | } 99 | 100 | { 101 | final String text = 102 | "[if lt IE 8]><[endif]"; 103 | final String se = "if lt IE 8"; 104 | final String c = ""; 105 | final String ee = "endif"; 106 | 107 | 108 | checkParsing(text, se, c, ee, true); 109 | } 110 | 111 | 112 | } 113 | 114 | 115 | 116 | 117 | 118 | private void checkParsing(final String text, 119 | final String expectedStartExpression, final String expectedContent, final String expectedEndExpression) 120 | throws Exception { 121 | checkParsing(text, expectedStartExpression, expectedContent, expectedEndExpression, false); 122 | } 123 | 124 | private void checkParsing(final String text, 125 | final String expectedStartExpression, final String expectedContent, final String expectedEndExpression, 126 | final boolean shouldFail) { 127 | 128 | final ConditionalCommentParsingResult result = 129 | ConditionalCommentUtils.parseConditionalComment(text); 130 | if (result == null) { 131 | if (!shouldFail) { 132 | assertTrue("Text \""+ text + "\" did not parse OK but should have", false); 133 | } 134 | } else if (shouldFail) { 135 | assertFalse("Text \""+ text + "\" parsed OK but should have failed", true); 136 | } 137 | 138 | if (result != null) { 139 | final String obtainedStartExpression = 140 | result.getText().substring(result.getStartExpressionOffset(), result.getStartExpressionOffset() + result.getStartExpressionLen()); 141 | final String obtainedContent = 142 | result.getText().substring(result.getContentOffset(), result.getContentOffset() + result.getContentLen()); 143 | final String obtainedEndExpression = 144 | result.getText().substring(result.getEndExpressionOffset(), result.getEndExpressionOffset() + result.getEndExpressionLen()); 145 | 146 | assertEquals(expectedStartExpression, obtainedStartExpression); 147 | assertEquals(expectedContent, obtainedContent); 148 | assertEquals(expectedEndExpression, obtainedEndExpression); 149 | } 150 | 151 | } 152 | 153 | 154 | 155 | } 156 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 4.0.0 24 | org.thymeleaf.extras 25 | thymeleaf-extras-conditionalcomments 26 | jar 27 | 2.1.3-SNAPSHOT 28 | thymeleaf-extras-conditionalcomments 29 | http://www.thymeleaf.org 30 | 31 | XML/XHTML/HTML5 template engine for Java 32 | 33 | 34 | 35 | The Apache Software License, Version 2.0 36 | http://www.apache.org/licenses/LICENSE-2.0.txt 37 | repo 38 | 39 | 40 | 41 | 42 | The THYMELEAF team 43 | http://www.thymeleaf.org 44 | 45 | 46 | 47 | scm:git:git@github.com:thymeleaf/thymeleaf-extras-conditionalcomments.git 48 | scm:git:git@github.com:thymeleaf/thymeleaf-extras-conditionalcomments.git 49 | scm:git:git@github.com:thymeleaf/thymeleaf-extras-conditionalcomments.git 50 | HEAD 51 | 52 | 53 | 54 | 55 | danielfernandez 56 | Daniel Fernandez 57 | daniel.fernandez AT 11thlabs DOT org 58 | 59 | Project Admin 60 | Lead Developer 61 | 62 | 63 | 64 | jmiguelsamper 65 | Jose Miguel Samper 66 | jmiguelsamper AT users DOT sourceforge DOT net 67 | 68 | Developer 69 | 70 | 71 | 72 | ultraq 73 | Emanuel Rabina 74 | emanuelrabina AT gmail DOT com 75 | 76 | Developer 77 | 78 | 79 | 80 | 81 | 82 | 83 | sonatype-nexus-snapshots 84 | Sonatype Nexus Snapshots 85 | http://oss.sonatype.org/content/repositories/snapshots 86 | 87 | 88 | sonatype-nexus-snapshots 89 | Sonatype Nexus Snapshots 90 | http://oss.sonatype.org/service/local/staging/deploy/maven2 91 | 92 | 93 | 94 | 95 | 96 | sonatype-nexus-snapshots 97 | Sonatype Nexus Snapshots 98 | https://oss.sonatype.org/content/repositories/snapshots 99 | 100 | true 101 | 102 | 103 | 104 | 105 | 106 | 107 | 1.5 108 | 1.5 109 | ISO-8859-1 110 | 2.1.3.RELEASE 111 | 2.0.0.RELEASE 112 | 1.6.6 113 | 4.8.2 114 | 1.2.15 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | src/main/resources 124 | 125 | 126 | 127 | . 128 | META-INF 129 | 130 | LICENSE.txt 131 | NOTICE.txt 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | src/test/resources 140 | 141 | 142 | 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-compiler-plugin 148 | 3.2 149 | 150 | ${maven.compile.source} 151 | ${maven.compile.target} 152 | 153 | 154 | 155 | 156 | org.apache.maven.plugins 157 | maven-resources-plugin 158 | 2.7 159 | 160 | US-ASCII 161 | 162 | 163 | 164 | 165 | org.apache.maven.plugins 166 | maven-jar-plugin 167 | 2.5 168 | 169 | 170 | false 171 | 172 | true 173 | true 174 | 175 | 176 | 177 | 178 | 179 | ${maven.compile.source} 180 | ${maven.compile.target} 181 | 182 | 183 | 184 | 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-javadoc-plugin 189 | 2.10.1 190 | 191 | java.lang 192 | ${basedir}/src/main/javadoc/overview.html 193 | ${basedir}/src/main/javadoc 194 | ${project.reporting.outputDirectory}/api/${project.artifactId}/apidocs 195 | 196 | 197 | 198 | package 199 | 200 | jar 201 | 202 | 203 | 204 | 205 | 206 | 207 | org.apache.maven.plugins 208 | maven-source-plugin 209 | 2.4 210 | 211 | 212 | package 213 | 214 | jar 215 | 216 | 217 | 218 | 219 | 220 | 221 | org.apache.maven.plugins 222 | maven-assembly-plugin 223 | 2.2-beta-5 224 | 225 | 226 | make-assembly-dist 227 | package 228 | 229 | attached 230 | 231 | 232 | 233 | ${basedir}/src/assembly/dist.xml 234 | 235 | true 236 | thymeleaf-extras-conditionalcomments-${project.version} 237 | 238 | 239 | 240 | 241 | 242 | 243 | org.apache.maven.plugins 244 | maven-gpg-plugin 245 | 1.1 246 | 247 | 248 | sign-artifacts 249 | verify 250 | 251 | sign 252 | 253 | 254 | 255 | 256 | 257 | 258 | org.apache.maven.plugins 259 | maven-release-plugin 260 | 2.5.2 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | org.thymeleaf 275 | thymeleaf 276 | ${thymeleaf.version} 277 | compile 278 | 279 | 280 | 281 | org.attoparser 282 | attoparser 283 | ${attoparser.version} 284 | compile 285 | 286 | 287 | 288 | org.slf4j 289 | slf4j-api 290 | ${slf4j.version} 291 | compile 292 | 293 | 294 | 295 | junit 296 | junit 297 | ${junit.version} 298 | test 299 | 300 | 301 | 302 | org.slf4j 303 | slf4j-log4j12 304 | ${slf4j.version} 305 | test 306 | 307 | 308 | 309 | log4j 310 | log4j 311 | ${log4j.version} 312 | test 313 | 314 | 315 | com.sun.jdmk 316 | jmxtools 317 | 318 | 319 | com.sun.jmx 320 | jmxri 321 | 322 | 323 | javax.jms 324 | jms 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | -------------------------------------------------------------------------------- /CONTRIBUTING.markdown: -------------------------------------------------------------------------------- 1 | Contributing to Thymeleaf: Terms and Conditions 2 | =============================================== 3 | 4 | ------------------------------------------------------------------------------ 5 | 6 | Do you want to contribute your work to Thymeleaf? Well, then first and most important: **THANK YOU!** 7 | 8 | Now, in order to accept your contribution, there are some terms you must expressly agree with, so please 9 | read them carefully. They may seem a bit cumbersome but they are there to protect you, your contribution, 10 | and most importantly, the project's future. 11 | 12 | **Important**: submitting any contributions to the Thymeleaf project implies your **full acceptance of these terms**, 13 | including the *"Thymeleaf Individual Contributor License Agreement"* detailed at the end of this document. 14 | 15 | 16 | Who can contribute? 17 | ------------------- 18 | 19 | Anyone, with the unique condition that he/she must be a **private individual**, acting in 20 | his/her own name, and not being endorsed in their contributed work by any company or government. 21 | 22 | Note that this condition will not only refer to the ownership of the effort invested in contributing 23 | to the project, but also to the fact that *no private or public company will be mentioned as a part 24 | of your contribution on the project's website or code*, including but not limited to web/email addresses 25 | or package names. 26 | 27 | 28 | What is the first step to be taken? 29 | ----------------------------------- 30 | 31 | First of all, **talk to the [project members](http://www.thymeleaf.org/team.html)** (an email should do) about 32 | your ideas: new features, fixes, documentation... whatever you would like to contribute to the project. Let us 33 | discuss the possibilities with you so that we make sure your contribution goes in the right direction and aligns 34 | with the project's standards, intentions and roadmap. 35 | 36 | 37 | How will your involvement with the Thymeleaf project work? 38 | ---------------------------------------------------------- 39 | 40 | All contributions are submitted in the form of GitHub *pull requests*. Note that contributors do not 41 | have read+write (or *pull+push*) access to the project repositories, only project *members* do. 42 | 43 | Also, please understand that *not all pull requests will be accepted and merged into the project's 44 | repositories*. Talk about your planned contributions with the project members before creating pull 45 | requests so you can maximize the possibility of your contributions being accepted. 46 | 47 | Once your contribution is approved, you will be listed as a *contributor* on the 48 | [Thymeleaf Team page](http://www.thymeleaf.org/team.html). You can opt-out of this if you want. 49 | Also, you will be `@author` for any new Java classes that you write and also co-`@author` of any 50 | existing classes to which you make significant changes. You can also opt-out of this if you want. 51 | 52 | 53 | About the code you contribute 54 | ----------------------------- 55 | 56 | ### General guidelines: 57 | 58 | - Obviously, **your code must both compile and work correctly**. Also, the addition of any new patches to the 59 | codebase should not render it unstable in any way. 60 | - All your code should be easy to read and understand by a human. 61 | - There should be no compilation warnings at all. 62 | 63 | ### Detailed Java code quality standards: 64 | 65 | - All your code should compile and run in **Java 6.0**. 66 | - All comments, names of classes and variables, log messages, etc. must be **in English**. 67 | - All `.java` files must include the standard Thymeleaf copyright header. 68 | - All your code should follow the Java Code Conventions regarding variable/method/class naming. 69 | - Maximum line length is 120 characters. 70 | - Indentation should be made with 4 spaces, not tabs. 71 | - Line feeds should be UNIX-like (`\n`). 72 | - All `.java` source files should be pure ASCII. All `.properties` files should be ISO-8859-1. 73 | - Number autoboxing and/or autounboxing is forbidden. 74 | - Every class should define a constructor, even if it is the no-argument constructor, and include a call to `super()`. 75 | - All method parameters should be declared as `final` so that they cannot be changed or reassigned in the method. 76 | - All non-nullable parameters in a public method should be first validated with a `Validate.notNull(...)` call. 77 | This maintains consistency in the behavior of public methods and the error message used. 78 | - Include a block comment (`/* ... */`) for any non-trivial algorithm you develop. *"Non-trivial"* usually means you 79 | had to make some design decisions to do things in a certain way. Your comment should explain *why* you wrote the 80 | code the way you wrote it. Do not write obvious comments that explain what the code does; the code should be clear 81 | and expressive enough so the *what* and *how* of it is obvious. 82 | - All public methods and classes directly available to users should have comprehensive JavaDoc comments. 83 | 84 | ### Detailed HTML/XML code quality standards: 85 | 86 | - All tags, CSS styles, file names, etc. must be **in English**. 87 | - Lower case should be preferred for HTML/XML artifacts. The only exceptions are `DOCTYPE` and `CDATA` clauses. 88 | - All HTML code should be XML-valid (i.e. all tags should be closed, attributes surrounded by commas, etc.) 89 | - Maximum line length is 120 characters. 90 | - Indentation should be made with 4 spaces, not tabs. 91 | - Line feeds should be UNIX-like (`\n`). 92 | - All `.html` and `.xml` source files should be pure ASCII, even if _content-type_ is set to a different encoding. 93 | - All XHTML self-closing (minimized) tags should have a space before `/>` (the XHTML standards say so!). 94 | - All inline scripts must be enclosed inside a commented `` block. 95 | 96 | 97 | About the documentation/articles you contribute 98 | ----------------------------------------------- 99 | 100 | Note the following only applies to documentation/articles meant to be published at the Thymeleaf website. 101 | 102 | - All documentation artifacts, including articles, must be written **in correct English**. 103 | - Your name and email will be displayed as *"author"* of any documentation artifacts you create. 104 | - Topic and text structure must be first discussed and agreed upon with the project members. 105 | - Project members may edit and make small changes to your texts—of which you will be informed—before 106 | publishing them. 107 | - Format and visual styles must adhere to the Thymeleaf website standards, of which you will be informed 108 | by the project members. 109 | 110 | 111 | Pay special attention to this 112 | ----------------------------- 113 | 114 | All Thymeleaf software is distributed under the **Apache License 2.0** open source license; your contributions 115 | will be licensed in the same way. 116 | 117 | If you work for a company which, by the way or place in which your code was written, by your contract terms 118 | or by the laws in your country, could claim any rights (including but not limited to intellectual or industrial 119 | property) over your contributed code, you will have to send the project members (either by email from your 120 | authorised superiors or by signed fax), a statement indicating that your company agrees with the terms 121 | explained in this page, and that it both authorises your contribution to Thymeleaf and states that it will 122 | never claim any kind of rights over it. 123 | 124 | 125 | Thymeleaf Individual Contributor License Agreement 126 | -------------------------------------------------- 127 | 128 | This contributor agreement ("Agreement") documents the rights granted by contributors to the Thymeleaf Project. 129 | 130 | This is a legally binding document, so please read it carefully before agreeing to it. The Agreement 131 | may cover more than one software project managed by Thymeleaf. 132 | 133 | 134 | ### 1. Definitions 135 | 136 | * _"Thymeleaf"_ means the "Thymeleaf Project organization and members". 137 | * _"You"_ means the individual who submits a Contribution to Thymeleaf. 138 | * _"Contribution"_ means any work of authorship that is submitted by you to Thymeleaf in which you own 139 | or assert ownership of the Copyright. 140 | * _"Copyright"_ means all rights protecting works of authorship owned or controlled by you, 141 | including copyright, moral and neighboring rights, as appropriate, for the full term of their 142 | existence including any extensions by you. 143 | * _"Material"_ means the work of authorship which is made available by Thymeleaf to third parties. When 144 | this Agreement covers more than one software project, the Material means the work of authorship 145 | to which the Contribution was submitted. After you submit the Contribution, it may be included 146 | in the Material. 147 | * _"Submit"_ means any form of electronic, verbal, or written communication sent to Thymeleaf or its 148 | representatives, including but not limited to electronic mailing lists, source code control systems, 149 | and issue tracking systems that are managed by, or on behalf of, Thymeleaf for the purpose of discussing 150 | and improving the Material, but excluding communication that is conspicuously marked or 151 | otherwise designated in writing by you as _"Not a Contribution."_ 152 | * _"Submission Date"_ means the date on which you submit a Contribution to Thymeleaf. 153 | * _"Effective Date"_ means the date you execute this agreement or the date You first submit a 154 | Contribution to Thymeleaf, whichever is earlier. 155 | 156 | 157 | ### 2. Grant of Rights 158 | 159 | #### 2.1. Copyright License 160 | 161 | * (a) You retain ownership of the copyright in your Contribution and have the same rights to use or 162 | license the Contribution which you would have had without entering into the agreement. 163 | * (b) To the maximum extent permitted by the relevant law, you grant to Thymeleaf a perpetual, worldwide, 164 | non-exclusive, transferable, royalty-free, irrevocable license under the copyright covering the 165 | Contribution, with the right to sublicense such rights through multiple tiers of sublicensees, to 166 | reproduce, modify, display, perform and distribute the Contribution as part of the Material; provided 167 | that this license is conditioned upon compliance with Section 2.3. 168 | 169 | #### 2.2 Patent License 170 | 171 | For patent claims including, without limitation, method, process, and apparatus claims which you 172 | own, control or have the right to grant, now or in the future, you grant to Thymeleaf a perpetual, worldwide, 173 | non-exclusive, transferable, royalty-free, irrevocable patent license, with the right to sublicense these 174 | rights to multiple tiers of sublicensees, to make, have made, use, sell, offer for sale, import and 175 | otherwise transfer the Contribution and the Contribution in combination with the Material (and 176 | portions of such combination). This license is granted only to the extent that the exercise of the 177 | licensed rights infringes such patent claims; and provided that this license is conditioned upon 178 | compliance with Section 2.3. 179 | 180 | #### 2.3 Outbound License 181 | 182 | As a condition on the grant of rights in Sections 2.1 and 2.2, Thymeleaf agrees to license the Contribution only 183 | under the terms of the Apache License 2.0 (including any right to adopt any future version of this license if 184 | permitted). 185 | 186 | #### 2.4 Moral Rights 187 | 188 | If moral rights apply to the Contribution, to the maximum extent permitted by law, you waive and agree not 189 | to assert such moral rights against Thymeleaf or its successors in interest, or any of our licensees, either 190 | direct or indirect. 191 | 192 | #### 2.5 Thymeleaf Rights 193 | 194 | You acknowledge that Thymeleaf is not obligated to use your Contribution as part of the 195 | Material and may decide to include any Contributions Thymeleaf considers appropriate. 196 | 197 | #### 2.6 Reservation of Rights 198 | 199 | Any rights not expressly assigned or licensed under this section are expressly reserved by you. 200 | 201 | 202 | ### 3. Agreement 203 | 204 | You confirm that: 205 | 206 | * (a) You have the legal authority to enter into this Agreement. 207 | * (b) You own the Copyright and patent claims covering the Contribution which are required to grant 208 | the rights under Section 2. 209 | * (c) The grant of rights under Section 2 does not violate any grant of rights which you have made to 210 | third parties, including your employer. If you are an employee, you have had your employer approve 211 | this Agreement. If you are less than eighteen years old, please have your parents or guardian 212 | sign the Agreement. 213 | 214 | 215 | ### 4. Disclaimer 216 | 217 | EXCEPT FOR THE EXPRESS WARRANTIES IN SECTION 3, THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, 218 | ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF MERCHANTABILITY, 219 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO THYMELEAF AND BY 220 | THYMELEAF TO YOU. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN 221 | DURATION TO THE MINIMUM PERIOD PERMITTED BY LAW. 222 | 223 | 224 | ### 5. Consequential Damage Waiver 225 | 226 | TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR THYMELEAF BE LIABLE FOR ANY LOSS OF 227 | PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY 228 | DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) 229 | UPON WHICH THE CLAIM IS BASED. 230 | 231 | 232 | ### 6. Miscellaneous 233 | 234 | * 6.1 This Agreement will be governed by and construed in accordance with the laws of Spain excluding its 235 | conflicts of law provisions. Under certain circumstances, the governing law in this section might be 236 | superseded by the United Nations Convention on Contracts for the International Sale of Goods ("UN 237 | Convention") and the parties intend to avoid the application of the UN Convention to this Agreement 238 | and, thus, exclude the application of the UN Convention in its entirety to this Agreement. 239 | * 6.2 This Agreement sets out the entire agreement between you and Thymeleaf for your Contributions to Thymeleaf 240 | and overrides all other agreements or understandings. 241 | * 6.3 If You or Thymeleaf assign the rights or obligations received through this Agreement to a third party, as a 242 | condition of the assignment, that third party must agree in writing to abide by all the rights and 243 | obligations in the Agreement. 244 | * 6.4 The failure of either party to require performance by the other party of any provision of this 245 | Agreement in one situation shall not affect the right of a party to require such performance at any time 246 | in the future. A waiver of performance under a provision in one situation shall not be considered a 247 | waiver of the performance of the provision in the future or a waiver of the provision in its entirety. 248 | * 6.5 If any provision of this Agreement is found void and unenforceable, such provision will be 249 | replaced to the extent possible with a provision that comes closest to the meaning of the original 250 | provision and which is enforceable. The terms and conditions set forth in this Agreement shall apply 251 | notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the 252 | maximum extent possible under law. 253 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/conditionalcomments/parser/ConditionalCommentAttoTemplateParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.conditionalcomments.parser; 21 | 22 | import java.io.Reader; 23 | import java.io.StringReader; 24 | import java.math.BigDecimal; 25 | import java.math.RoundingMode; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.Stack; 29 | 30 | import org.attoparser.AbstractMarkupHandler; 31 | import org.attoparser.IMarkupParser; 32 | import org.attoparser.MarkupParser; 33 | import org.attoparser.ParseException; 34 | import org.attoparser.config.ParseConfiguration; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | import org.thymeleaf.Configuration; 38 | import org.thymeleaf.TemplateEngine; 39 | import org.thymeleaf.dom.CDATASection; 40 | import org.thymeleaf.dom.Comment; 41 | import org.thymeleaf.dom.DocType; 42 | import org.thymeleaf.dom.Document; 43 | import org.thymeleaf.dom.Element; 44 | import org.thymeleaf.dom.Element.RepresentationInTemplate; 45 | import org.thymeleaf.dom.NestableNode; 46 | import org.thymeleaf.dom.Node; 47 | import org.thymeleaf.dom.Text; 48 | import org.thymeleaf.exceptions.TemplateInputException; 49 | import org.thymeleaf.exceptions.TemplateProcessingException; 50 | import org.thymeleaf.templateparser.ITemplateParser; 51 | import org.thymeleaf.templateparser.TemplatePreprocessingReader; 52 | 53 | /** 54 | *

55 | * Parses Thymeleaf templates, using attoparser. 56 | *

57 | * 58 | * @author Daniel Fernández 59 | * 60 | */ 61 | public class ConditionalCommentAttoTemplateParser implements ITemplateParser { 62 | 63 | 64 | 65 | static final ParseConfiguration HTML_PARSING_CONFIGURATION; 66 | private static final IMarkupParser parser; 67 | 68 | 69 | 70 | static { 71 | HTML_PARSING_CONFIGURATION = ParseConfiguration.htmlConfiguration(); 72 | HTML_PARSING_CONFIGURATION.setUniqueAttributesInElementRequired(true); 73 | parser = new MarkupParser(HTML_PARSING_CONFIGURATION); 74 | } 75 | 76 | 77 | 78 | public ConditionalCommentAttoTemplateParser() { 79 | super(); 80 | } 81 | 82 | 83 | 84 | 85 | public final Document parseTemplate(final Configuration configuration, final String documentName, final Reader reader) { 86 | 87 | final TemplatePreprocessingReader templateReader = getTemplatePreprocessingReader(reader); 88 | 89 | try { 90 | 91 | return doParse(documentName, templateReader); 92 | 93 | } catch (final TemplateProcessingException e) { 94 | throw e; 95 | } catch (final ParseException e) { 96 | String message = null; 97 | if (documentName == null) { 98 | message = 99 | String.format("Exception parsing unnamed document or fragment: line %d - column %d", 100 | e.getLine(), e.getCol()); 101 | } else { 102 | message = 103 | String.format("Exception parsing document: template=\"%s\", line %d - column %d", 104 | documentName, e.getLine(), e.getCol()); 105 | } 106 | throw new TemplateInputException(message, e); 107 | } 108 | 109 | } 110 | 111 | 112 | 113 | 114 | private static Document doParse(final String documentName, final TemplatePreprocessingReader reader) 115 | throws ParseException { 116 | 117 | final TemplateMarkupHandler handler = new TemplateMarkupHandler(documentName); 118 | 119 | parser.parse(reader, handler); 120 | 121 | final String docTypeClause = reader.getDocTypeClause(); 122 | final String docTypeRootElementName = handler.getDocTypeRootElementName(); 123 | final String docTypePublicId = handler.getDocTypePublicId(); 124 | final String docTypeSystemId = handler.getDocTypeSystemId(); 125 | 126 | // The DOCTYPE root element name could be null if we are parsing 127 | // a non-complete document, a fragment, without a DOCTYPE declaration. 128 | final DocType docType = 129 | (docTypeRootElementName != null? 130 | new DocType(docTypeRootElementName, docTypePublicId, docTypeSystemId, docTypeClause) : 131 | null); 132 | 133 | final List rootNodes = handler.getRootNodes(); 134 | 135 | final String xmlVersion = handler.getXmlVersion(); 136 | final String xmlEncoding = handler.getXmlEncoding(); 137 | final boolean xmlStandalone = handler.isXmlStandalone(); 138 | 139 | final Document document = new Document(documentName, docType); 140 | 141 | if (xmlVersion != null) { 142 | document.setNodeProperty(Node.NODE_PROPERTY_XML_VERSION, xmlVersion); 143 | } 144 | 145 | if (xmlEncoding != null) { 146 | document.setNodeProperty(Node.NODE_PROPERTY_XML_ENCODING, xmlEncoding); 147 | } 148 | 149 | if (xmlStandalone) { 150 | document.setNodeProperty(Node.NODE_PROPERTY_XML_STANDALONE, Boolean.TRUE); 151 | } 152 | 153 | document.setChildren(rootNodes); 154 | 155 | return document; 156 | 157 | } 158 | 159 | 160 | 161 | 162 | 163 | public final List parseFragment(final Configuration configuration, final String fragment) { 164 | final Document document = 165 | parseTemplate( 166 | configuration, 167 | null, // documentName 168 | new StringReader(fragment)); 169 | return document.getChildren(); 170 | } 171 | 172 | 173 | 174 | public final List parseFragment(final Configuration configuration, final String text, final int offset, final int len) { 175 | final Document document = 176 | parseTemplate( 177 | configuration, 178 | null, // documentName 179 | new StringReader(text.substring(offset, offset + len))); 180 | return document.getChildren(); 181 | } 182 | 183 | 184 | 185 | 186 | protected boolean shouldAddThymeleafRootToParser() { 187 | return true; 188 | } 189 | 190 | 191 | 192 | protected TemplatePreprocessingReader getTemplatePreprocessingReader(final Reader reader) { 193 | if (reader instanceof TemplatePreprocessingReader) { 194 | final TemplatePreprocessingReader templatePreprocessingReader = (TemplatePreprocessingReader) reader; 195 | return new TemplatePreprocessingReader( 196 | templatePreprocessingReader.getInnerReader(), 8192, shouldAddThymeleafRootToParser()); 197 | } 198 | return new TemplatePreprocessingReader(reader, 8192, shouldAddThymeleafRootToParser()); 199 | } 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | private static final class TemplateMarkupHandler extends AbstractMarkupHandler { 208 | 209 | private static final Logger logger = LoggerFactory.getLogger(ConditionalCommentAttoTemplateParser.class); 210 | 211 | private final String documentName; 212 | private final Stack elementStack; 213 | 214 | /* 215 | * TODO Should be an AttributeHolder instead of an Element in >= 2.1 216 | */ 217 | private Element currentElement = null; 218 | 219 | private List rootNodes = null; 220 | 221 | private String docTypeRootElementName = null; 222 | private String docTypePublicId = null; 223 | private String docTypeSystemId = null; 224 | 225 | private String xmlEncoding = null; 226 | private String xmlVersion = null; 227 | private boolean xmlStandalone = false; 228 | 229 | 230 | public TemplateMarkupHandler(final String documentName) { 231 | 232 | super(); 233 | 234 | this.documentName = documentName; 235 | 236 | this.elementStack = new Stack(); 237 | this.rootNodes = new ArrayList(); 238 | 239 | } 240 | 241 | 242 | 243 | public String getDocTypeRootElementName() { 244 | return this.docTypeRootElementName; 245 | } 246 | 247 | public String getDocTypePublicId() { 248 | return this.docTypePublicId; 249 | } 250 | 251 | public String getDocTypeSystemId() { 252 | return this.docTypeSystemId; 253 | } 254 | 255 | public List getRootNodes() { 256 | return this.rootNodes; 257 | } 258 | 259 | public String getXmlEncoding() { 260 | return this.xmlEncoding; 261 | } 262 | 263 | public String getXmlVersion() { 264 | return this.xmlVersion; 265 | } 266 | 267 | public boolean isXmlStandalone() { 268 | return this.xmlStandalone; 269 | } 270 | 271 | 272 | 273 | /* 274 | * ----------------- 275 | * Document handling 276 | * ----------------- 277 | */ 278 | 279 | 280 | @Override 281 | public void handleDocumentEnd( 282 | final long endTimeNanos, final long totalTimeNanos, 283 | final int line, final int col) 284 | throws ParseException { 285 | 286 | super.handleDocumentEnd(endTimeNanos, totalTimeNanos, line, col); 287 | 288 | if (logger.isTraceEnabled()) { 289 | final BigDecimal elapsed = BigDecimal.valueOf(totalTimeNanos); 290 | final BigDecimal elapsedMs = elapsed.divide(BigDecimal.valueOf(1000000), RoundingMode.HALF_UP); 291 | if (this.documentName == null) { 292 | logger.trace("[THYMELEAF][{}][{}][{}] Parsed unnamed template or fragment in {} nanoseconds (approx. {}ms)", 293 | new Object[] {TemplateEngine.threadIndex(), 294 | elapsed, elapsedMs, 295 | elapsed, elapsedMs}); 296 | } else { 297 | logger.trace("[THYMELEAF][{}][{}][{}][{}] Parsed template \"{}\" in {} nanoseconds (approx. {}ms)", 298 | new Object[] {TemplateEngine.threadIndex(), 299 | this.documentName, elapsed, elapsedMs, 300 | this.documentName, elapsed, elapsedMs}); 301 | } 302 | } 303 | 304 | } 305 | 306 | 307 | 308 | 309 | /* 310 | * ------------------------ 311 | * XML Declaration handling 312 | * ------------------------ 313 | */ 314 | 315 | 316 | @Override 317 | public void handleXmlDeclaration( 318 | final char[] buffer, 319 | final int keywordOffset, final int keywordLen, 320 | final int keywordLine, final int keywordCol, 321 | final int versionOffset, final int versionLen, 322 | final int versionLine, final int versionCol, 323 | final int encodingOffset, final int encodingLen, 324 | final int encodingLine, final int encodingCol, 325 | final int standaloneOffset, final int standaloneLen, 326 | final int standaloneLine, final int standaloneCol, 327 | final int outerOffset, final int outerLen, 328 | final int line, final int col) 329 | throws ParseException { 330 | 331 | super.handleXmlDeclaration(buffer, keywordOffset, keywordLen, 332 | keywordLine, keywordCol, versionOffset, versionLen, versionLine, 333 | versionCol, encodingOffset, encodingLen, encodingLine, encodingCol, 334 | standaloneOffset, standaloneLen, standaloneLine, standaloneCol, 335 | outerOffset, outerLen, line, col); 336 | 337 | if (versionLen > 0) { 338 | this.xmlVersion = new String(buffer, versionOffset, versionLen); 339 | } 340 | if (encodingLen > 0) { 341 | this.xmlEncoding = new String(buffer, encodingOffset, encodingLen); 342 | } 343 | if (standaloneLen > 0) { 344 | this.xmlStandalone = Boolean.parseBoolean(new String(buffer, standaloneOffset, standaloneLen)); 345 | } 346 | 347 | } 348 | 349 | 350 | 351 | 352 | /* 353 | * ---------------- 354 | * DOCTYPE handling 355 | * ---------------- 356 | */ 357 | 358 | 359 | @Override 360 | public void handleDocType( 361 | final char[] buffer, 362 | final int keywordOffset, final int keywordLen, 363 | final int keywordLine, final int keywordCol, 364 | final int elementNameOffset, final int elementNameLen, 365 | final int elementNameLine, final int elementNameCol, 366 | final int typeOffset, final int typeLen, 367 | final int typeLine, final int typeCol, 368 | final int publicIdOffset, final int publicIdLen, 369 | final int publicIdLine, final int publicIdCol, 370 | final int systemIdOffset, final int systemIdLen, 371 | final int systemIdLine, final int systemIdCol, 372 | final int internalSubsetOffset, final int internalSubsetLen, 373 | final int internalSubsetLine, final int internalSubsetCol, 374 | final int outerOffset, final int outerLen, 375 | final int outerLine, final int outerCol) 376 | throws ParseException { 377 | 378 | super.handleDocType(buffer, keywordOffset, keywordLen, keywordLine, keywordCol, 379 | elementNameOffset, elementNameLen, elementNameLine, elementNameCol, 380 | typeOffset, typeLen, typeLine, typeCol, publicIdOffset, publicIdLen, 381 | publicIdLine, publicIdCol, systemIdOffset, systemIdLen, systemIdLine, 382 | systemIdCol, internalSubsetOffset, internalSubsetLen, 383 | internalSubsetLine, internalSubsetCol, outerOffset, outerLen, 384 | outerLine, outerCol); 385 | 386 | if (elementNameLen > 0) { 387 | this.docTypeRootElementName = new String(buffer, elementNameOffset, elementNameLen); 388 | } 389 | if (publicIdLen > 0) { 390 | this.docTypePublicId = new String(buffer, publicIdOffset, publicIdLen); 391 | } 392 | if (systemIdLen > 0) { 393 | this.docTypeSystemId = new String(buffer, systemIdOffset, systemIdLen); 394 | } 395 | 396 | } 397 | 398 | 399 | 400 | 401 | /* 402 | * ---------------------- 403 | * CDATA Section handling 404 | * ---------------------- 405 | */ 406 | 407 | 408 | @Override 409 | public void handleCDATASection( 410 | final char[] buffer, 411 | final int contentOffset, final int contentLen, 412 | final int outerOffset, final int outerLen, 413 | final int line, final int col) 414 | throws ParseException { 415 | 416 | super.handleCDATASection(buffer, contentOffset, contentLen, outerOffset, outerLen, line, col); 417 | 418 | final String content = new String(buffer, contentOffset, contentLen); 419 | 420 | TemplatePreprocessingReader.removeEntitySubstitutions(buffer, 0, contentLen); 421 | 422 | final Node cdata = new CDATASection(content, null, null, true); 423 | 424 | if (this.elementStack.isEmpty()) { 425 | this.rootNodes.add(cdata); 426 | } else { 427 | final NestableNode parent = this.elementStack.peek(); 428 | parent.addChild(cdata); 429 | } 430 | 431 | } 432 | 433 | 434 | 435 | 436 | /* 437 | * ------------- 438 | * Text handling 439 | * ------------- 440 | */ 441 | 442 | 443 | @Override 444 | public void handleText( 445 | final char[] buffer, 446 | final int offset, final int len, 447 | final int line, final int col) 448 | throws ParseException { 449 | 450 | super.handleText(buffer, offset, len, line, col); 451 | 452 | TemplatePreprocessingReader.removeEntitySubstitutions(buffer, offset, len); 453 | 454 | final String content = new String(buffer, offset, len); 455 | 456 | final Node textNode = new Text(content, null, null, true); 457 | 458 | if (this.elementStack.isEmpty()) { 459 | this.rootNodes.add(textNode); 460 | } else { 461 | final NestableNode parent = this.elementStack.peek(); 462 | parent.addChild(textNode); 463 | } 464 | 465 | } 466 | 467 | 468 | 469 | 470 | /* 471 | * ---------------- 472 | * Comment handling 473 | * ---------------- 474 | */ 475 | 476 | 477 | @Override 478 | public void handleComment( 479 | final char[] buffer, 480 | final int contentOffset, final int contentLen, 481 | final int outerOffset, final int outerLen, 482 | final int line, final int col) 483 | throws ParseException { 484 | 485 | super.handleComment(buffer, contentOffset, contentLen, outerOffset, outerLen, line, col); 486 | 487 | final String content = new String(buffer, contentOffset, contentLen); 488 | 489 | final Comment comment = new Comment(content); 490 | 491 | if (this.elementStack.isEmpty()) { 492 | this.rootNodes.add(comment); 493 | } else { 494 | final NestableNode parent = this.elementStack.peek(); 495 | parent.addChild(comment); 496 | } 497 | 498 | } 499 | 500 | 501 | 502 | /* 503 | * ---------------- 504 | * Element handling 505 | * ---------------- 506 | */ 507 | 508 | 509 | @Override 510 | public void handleAttribute( 511 | final char[] buffer, 512 | final int nameOffset, final int nameLen, 513 | final int nameLine, final int nameCol, 514 | final int operatorOffset, final int operatorLen, 515 | final int operatorLine, final int operatorCol, 516 | final int valueContentOffset, final int valueContentLen, 517 | final int valueOuterOffset, final int valueOuterLen, 518 | final int valueLine, final int valueCol) 519 | throws ParseException { 520 | 521 | super.handleAttribute(buffer, nameOffset, nameLen, nameLine, nameCol, 522 | operatorOffset, operatorLen, operatorLine, operatorCol, 523 | valueContentOffset, valueContentLen, valueOuterOffset, valueOuterLen, 524 | valueLine, valueCol); 525 | 526 | final String attributeName = new String(buffer, nameOffset, nameLen); 527 | 528 | // This operates directly on the buffer but it's alright (no real need to duplicate it) 529 | TemplatePreprocessingReader.removeEntitySubstitutions(buffer, valueContentOffset, valueContentLen); 530 | final String attributeValue = new String(buffer, valueContentOffset, valueContentLen); 531 | 532 | this.currentElement.setAttribute( 533 | attributeName, false, attributeValue, true); 534 | 535 | } 536 | 537 | 538 | 539 | 540 | @Override 541 | public void handleStandaloneElementStart( 542 | final char[] buffer, 543 | final int offset, final int len, 544 | final boolean minimized, 545 | final int line, final int col) 546 | throws ParseException { 547 | 548 | final String elementName = new String(buffer, offset, len); 549 | 550 | final Element element = 551 | new Element(elementName, this.documentName, Integer.valueOf(line), RepresentationInTemplate.STANDALONE); 552 | this.currentElement = element; 553 | 554 | if (this.elementStack.isEmpty()) { 555 | this.rootNodes.add(element); 556 | } else { 557 | final NestableNode parent = this.elementStack.peek(); 558 | parent.addChild(element); 559 | } 560 | 561 | } 562 | 563 | 564 | 565 | 566 | @Override 567 | public void handleOpenElementStart( 568 | final char[] buffer, 569 | final int offset, final int len, 570 | final int line, final int col) 571 | throws ParseException { 572 | 573 | super.handleOpenElementStart(buffer, offset, len, line, col); 574 | 575 | final String elementName = new String(buffer, offset, len); 576 | 577 | final Element element = 578 | new Element(elementName, this.documentName, Integer.valueOf(line), RepresentationInTemplate.ONLY_OPEN); 579 | this.currentElement = element; 580 | 581 | this.elementStack.push(element); 582 | 583 | } 584 | 585 | 586 | 587 | 588 | @Override 589 | public void handleCloseElementStart( 590 | final char[] buffer, 591 | final int offset, final int len, 592 | final int line, final int col) 593 | throws ParseException { 594 | 595 | super.handleCloseElementStart(buffer, offset, len, line, col); 596 | 597 | final String closedElementName = new String(buffer, offset, len); 598 | searchInStack(closedElementName); 599 | 600 | // We are sure this is the node we want to close 601 | final NestableNode node = this.elementStack.pop(); 602 | 603 | if (node instanceof Element) { 604 | 605 | final Element element = (Element) node; 606 | 607 | // Adjust the representation in template. Differentiating between being 608 | // empty or not will allow a more correct output behaviour if children are 609 | // added or removed. 610 | if (element.hasChildren()) { 611 | element.setRepresentationInTemplate(RepresentationInTemplate.OPEN_AND_CLOSE_NONEMPTY); 612 | } else { 613 | element.setRepresentationInTemplate(RepresentationInTemplate.OPEN_AND_CLOSE_EMPTY); 614 | } 615 | 616 | 617 | if (TemplatePreprocessingReader.SYNTHETIC_ROOT_ELEMENT_NAME.equals(element.getOriginalName())) { 618 | // If it is the synthetic root element, then we skip the element itself and just add 619 | // its children to the results. 620 | final List syntheticRootChildren = element.getChildren(); 621 | if (this.elementStack.isEmpty()) { 622 | this.rootNodes.addAll(syntheticRootChildren); 623 | } else { 624 | final NestableNode parent = this.elementStack.peek(); 625 | for (final Node syntheticRootChild : syntheticRootChildren) { 626 | parent.addChild(syntheticRootChild); 627 | } 628 | } 629 | // End the handler 630 | return; 631 | } 632 | 633 | } 634 | 635 | if (this.elementStack.isEmpty()) { 636 | this.rootNodes.add(node); 637 | } else { 638 | final NestableNode parent = this.elementStack.peek(); 639 | parent.addChild(node); 640 | } 641 | 642 | } 643 | 644 | 645 | 646 | 647 | private void searchInStack(final String soughtElementName) { 648 | 649 | NestableNode node = this.elementStack.peek(); 650 | 651 | while (true) { 652 | 653 | if (node instanceof Element) { 654 | 655 | final Element element = (Element) node; 656 | final String elementName = element.getOriginalName(); 657 | 658 | if (soughtElementName.equals(elementName)) { 659 | return; 660 | } 661 | 662 | } 663 | 664 | // unbalancedNode == node, but we need to pop from stack 665 | final NestableNode unbalancedNode = this.elementStack.pop(); 666 | 667 | if (this.elementStack.isEmpty()) { 668 | // Can never happen because of the parser's Document Restrictions 669 | // (no unbalanced close tags are allowed) 670 | throw new TemplateInputException("Unbalanced close tag \"" + soughtElementName + "\""); 671 | } 672 | 673 | final NestableNode parent = this.elementStack.peek(); 674 | parent.addChild(unbalancedNode); 675 | 676 | node = parent; 677 | 678 | } 679 | 680 | } 681 | 682 | 683 | } 684 | 685 | 686 | 687 | 688 | } 689 | --------------------------------------------------------------------------------