001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 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 public class MultilinesDocTokenizer extends Tokenizer { 025 026 private static final String COMMENT_STARTED_ON_PREVIOUS_LINE = "COMMENT_STARTED_ON_PREVIOUS_LINE"; 027 private static final String COMMENT_TOKENIZER = "MULTILINE_COMMENT_TOKENIZER"; 028 private final char[] startToken; 029 private final char[] endToken; 030 031 /** 032 * @deprecated endToken is hardcoded to star-slash, whatever the startToken ! 033 */ 034 @Deprecated 035 public MultilinesDocTokenizer(String startToken, String tagBefore, String tagAfter) { 036 this(startToken, "*/", tagBefore, tagAfter); 037 } 038 039 public MultilinesDocTokenizer(String startToken, String endToken, String tagBefore, String tagAfter) { 040 super(tagBefore, tagAfter); 041 if (endToken.length() > CodeReader.LAST_CHARS_MAX_SIZE) { 042 throw new IllegalArgumentException("Does not currently support endToken longer than " + CodeReader.LAST_CHARS_MAX_SIZE + " characters"); 043 } 044 this.startToken = startToken.toCharArray(); 045 this.endToken = endToken.toCharArray(); 046 } 047 048 @Override 049 public boolean hasNextToken(CodeReader code) { 050 return code.peek() != '\n' && code.peek() != '\r' 051 && (isCommentStartedOnPreviousLine(code) || (code.peek() == startToken[0] && Arrays.equals(code.peek(startToken.length), startToken))); 052 } 053 054 @Override 055 public String nextToken(CodeReader code) { 056 return code.popTo(new MultilineEndTokenMatcher(code)); 057 } 058 059 private class MultilineEndTokenMatcher implements EndTokenMatcher { 060 CodeReader code; 061 062 public MultilineEndTokenMatcher(CodeReader code) { 063 this.code = code; 064 } 065 066 public boolean match(int endFlag) { 067 if (code.lastChar() == endToken[endToken.length - 1]) { 068 boolean matches = true; 069 070 for (int i = 1; i < endToken.length; i++) { 071 if (code.lastChar(i) != endToken[endToken.length - i - 1]) { 072 matches = false; 073 break; 074 } 075 } 076 if (matches) { 077 setCommentStartedOnPreviousLine(code, Boolean.FALSE); 078 return true; 079 } 080 } 081 if (endFlag == '\r' || endFlag == '\n') { 082 setCommentStartedOnPreviousLine(code, Boolean.TRUE); 083 return true; 084 } 085 return false; 086 } 087 } 088 089 private boolean isCommentStartedOnPreviousLine(CodeReader reader) { 090 Boolean b = (Boolean) reader.getVariable(COMMENT_STARTED_ON_PREVIOUS_LINE, Boolean.FALSE); 091 return (b == Boolean.TRUE) && (getTokenizerId().equals(reader.getVariable(COMMENT_TOKENIZER))); 092 } 093 094 private void setCommentStartedOnPreviousLine(CodeReader reader, Boolean b) { 095 reader.setVariable(COMMENT_STARTED_ON_PREVIOUS_LINE, b); 096 reader.setVariable(COMMENT_TOKENIZER, b ? getTokenizerId() : null); 097 } 098 099 private String getTokenizerId() { 100 return getClass().getSimpleName(); 101 } 102 }