001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.colorizer;
021
022 import java.util.Arrays;
023
024 import org.sonar.channel.CodeReader;
025 import org.sonar.channel.EndMatcher;
026
027 public class MultilinesDocTokenizer extends Tokenizer {
028
029 private static final String COMMENT_STARTED_ON_PREVIOUS_LINE = "COMMENT_STARTED_ON_PREVIOUS_LINE";
030 private static final String COMMENT_TOKENIZER = "MULTILINE_COMMENT_TOKENIZER";
031 private final char[] startToken;
032 private final char[] endToken;
033 private final String tagBefore;
034 private final String tagAfter;
035
036 /**
037 * @deprecated endToken is hardcoded to star-slash, whatever the startToken !
038 */
039 @Deprecated
040 public MultilinesDocTokenizer(String startToken, String tagBefore, String tagAfter) {
041 this(startToken, "*/", tagBefore, tagAfter);
042 }
043
044 public MultilinesDocTokenizer(String startToken, String endToken, String tagBefore, String tagAfter) {
045 this.tagBefore = tagBefore;
046 this.tagAfter = tagAfter;
047 this.startToken = startToken.toCharArray();
048 this.endToken = endToken.toCharArray();
049 }
050
051 public boolean hasNextToken(CodeReader code, HtmlCodeBuilder codeBuilder) {
052 return code.peek() != '\n'
053 && code.peek() != '\r'
054 && (isCommentStartedOnPreviousLine(codeBuilder) || (code.peek() == startToken[0] && Arrays.equals(code.peek(startToken.length),
055 startToken)));
056 }
057
058 public boolean consume(CodeReader code, HtmlCodeBuilder codeBuilder) {
059 if (hasNextToken(code, codeBuilder)) {
060 codeBuilder.appendWithoutTransforming(tagBefore);
061 code.popTo(new MultilineEndTokenMatcher(codeBuilder), codeBuilder);
062 codeBuilder.appendWithoutTransforming(tagAfter);
063 return true;
064 } else {
065 return false;
066 }
067 }
068
069 private class MultilineEndTokenMatcher implements EndMatcher {
070
071 private final HtmlCodeBuilder code;
072 private final StringBuilder colorizedCode;
073 private int commentSize = 0;
074
075 public MultilineEndTokenMatcher(HtmlCodeBuilder code) {
076 this.code = code;
077 this.colorizedCode = code.getColorizedCode();
078 }
079
080 public boolean match(int endFlag) {
081 commentSize++;
082 if (commentSize >= endToken.length + startToken.length || (commentSize >= endToken.length && isCommentStartedOnPreviousLine(code))) {
083 boolean matches = true;
084 for (int i = 1; i <= endToken.length; i++) {
085 if (colorizedCode.charAt(colorizedCode.length() - i) != endToken[endToken.length - i]) {
086 matches = false;
087 break;
088 }
089 }
090 if (matches) {
091 setCommentStartedOnPreviousLine(code, Boolean.FALSE);
092 return true;
093 }
094 }
095
096 if (endFlag == '\r' || endFlag == '\n') {
097 setCommentStartedOnPreviousLine(code, Boolean.TRUE);
098 return true;
099 }
100 return false;
101 }
102 }
103
104 private boolean isCommentStartedOnPreviousLine(HtmlCodeBuilder codeBuilder) {
105 Boolean b = (Boolean) codeBuilder.getVariable(COMMENT_STARTED_ON_PREVIOUS_LINE, Boolean.FALSE);
106 return (b == Boolean.TRUE) && (getTokenizerId().equals(codeBuilder.getVariable(COMMENT_TOKENIZER)));
107 }
108
109 private void setCommentStartedOnPreviousLine(HtmlCodeBuilder codeBuilder, Boolean b) {
110 codeBuilder.setVariable(COMMENT_STARTED_ON_PREVIOUS_LINE, b);
111 codeBuilder.setVariable(COMMENT_TOKENIZER, b ? getTokenizerId() : null);
112 }
113
114 private String getTokenizerId() {
115 return getClass().getSimpleName();
116 }
117 }