| /******************************************************************************* |
| * Copyright (c) 2000, 2010 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.formatter; |
| |
| import org.eclipse.jdt.internal.formatter.comment.IJavaDocTagConstants; |
| |
| /** |
| * Represents text inside a javadoc comment block. |
| * <p> |
| * Text may be simple as <code>Line inside a javadoc comment block</code> |
| * or may be a html tag. Note that to minimize memory footprint, only text |
| * positions are stored. |
| * </p><p> |
| * Simple text may have one or several lines. When it has several lines, the |
| * positions of the line breaks are also stored in the {@link #separators} array. |
| * </p><p> |
| * When text has html tags, then they are stored in {@link #htmlNodes} array |
| * in a recursive way. |
| * </p> |
| */ |
| public class FormatJavadocText extends FormatJavadocNode implements IJavaDocTagConstants { |
| |
| long[] separators; |
| int separatorsPtr = -1; |
| private int htmlTagIndex = -1; |
| boolean immutable = false; |
| FormatJavadocNode[] htmlNodes; |
| int[] htmlIndexes; |
| int htmlNodesPtr = -1; |
| int depth = 0; |
| |
| public FormatJavadocText(int start, int end, int line, int htmlIndex, int htmlDepth) { |
| super(start, end, line); |
| this.htmlTagIndex = htmlIndex; |
| this.depth = htmlDepth; |
| } |
| |
| /* |
| * Append a text to current one. |
| * If the given text is not an html tag or is a closing tag, then just append to |
| * the current text recording the separators. Otherwise, create a new html tag |
| * child node. |
| */ |
| void appendText(FormatJavadocText text) { |
| text.immutable = this.immutable; |
| if (this.depth == text.depth) { |
| addSeparator(text); |
| this.sourceEnd = text.sourceEnd; |
| if (text.isClosingHtmlTag()) { |
| // close the tag |
| this.htmlTagIndex = text.htmlTagIndex; |
| } |
| } else { |
| appendNode(text); |
| } |
| if (text.isHtmlTag()) { |
| switch (text.htmlTagIndex & JAVADOC_TAGS_ID_MASK) { |
| case JAVADOC_CODE_TAGS_ID: |
| text.linesBefore = this.htmlNodesPtr == -1 ? 0 : 2; |
| break; |
| case JAVADOC_SEPARATOR_TAGS_ID: |
| text.linesBefore = 1; |
| break; |
| case JAVADOC_SINGLE_BREAK_TAG_ID: |
| if (!text.isClosingHtmlTag()) text.linesBefore = 1; |
| break; |
| case JAVADOC_BREAK_TAGS_ID: |
| if (!text.isClosingHtmlTag()) text.linesBefore = 1; |
| } |
| } |
| } |
| void appendNode(FormatJavadocNode node) { |
| if (++this.htmlNodesPtr == 0) { // lazy initialization |
| this.htmlNodes = new FormatJavadocNode[DEFAULT_ARRAY_SIZE]; |
| } else { |
| if (this.htmlNodesPtr == this.htmlNodes.length) { |
| int size = this.htmlNodesPtr + DEFAULT_ARRAY_SIZE; |
| System.arraycopy(this.htmlNodes, 0, (this.htmlNodes= new FormatJavadocNode[size]), 0, this.htmlNodesPtr); |
| } |
| } |
| addSeparator(node); |
| this.htmlNodes[this.htmlNodesPtr] = node; |
| this.sourceEnd = node.sourceEnd; |
| } |
| |
| private void addSeparator(FormatJavadocNode node) { |
| // Just append the text |
| if (++this.separatorsPtr == 0) { // lazy initialization |
| this.separators = new long[DEFAULT_ARRAY_SIZE]; |
| this.htmlIndexes = new int[DEFAULT_ARRAY_SIZE]; |
| } else { // resize if needed |
| if (this.separatorsPtr == this.separators.length) { |
| int size = this.separatorsPtr + DEFAULT_ARRAY_SIZE; |
| System.arraycopy(this.separators, 0, (this.separators = new long[size]), 0, this.separatorsPtr); |
| System.arraycopy(this.htmlIndexes, 0, (this.htmlIndexes = new int[size]), 0, this.separatorsPtr); |
| } |
| } |
| this.separators[this.separatorsPtr] = (((long) this.sourceEnd) << 32) + node.sourceStart; |
| this.htmlIndexes[this.separatorsPtr] = node.isText() ? ((FormatJavadocText)node).htmlTagIndex : -1; |
| } |
| |
| void clean() { |
| int length = this.separators == null ? 0 : this.separators.length; |
| if (this.separatorsPtr != (length-1)) { |
| System.arraycopy(this.separators, 0, this.separators = new long[this.separatorsPtr+1], 0, this.separatorsPtr+1); |
| System.arraycopy(this.htmlIndexes, 0, this.htmlIndexes = new int[this.separatorsPtr+1], 0, this.separatorsPtr+1); |
| } |
| length = this.htmlNodes == null ? 0 : this.htmlNodes.length; |
| if (this.htmlNodesPtr != (length-1)) { |
| System.arraycopy(this.htmlNodes, 0, this.htmlNodes = new FormatJavadocNode[this.htmlNodesPtr+1], 0, this.htmlNodesPtr+1); |
| for (int i=0; i<=this.htmlNodesPtr; i++) { |
| this.htmlNodes[i].clean(); |
| } |
| } |
| } |
| |
| void closeTag() { |
| this.htmlTagIndex |= JAVADOC_CLOSED_TAG; |
| } |
| |
| int getHtmlTagIndex() { |
| return this.htmlTagIndex & JAVADOC_TAGS_INDEX_MASK; |
| } |
| |
| int getHtmlTagID() { |
| return this.htmlTagIndex & JAVADOC_TAGS_ID_MASK; |
| } |
| |
| FormatJavadocNode getLastNode() { |
| if (this.htmlNodes != null) { |
| return this.htmlNodes[this.htmlNodesPtr]; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns whether the text is a closing html tag or not. |
| * |
| * @return <code>true</code> if the node is an html tag and has '/' before its |
| * name (e.g. </bla>), <code>false</code> otherwise. |
| */ |
| public boolean isClosingHtmlTag() { |
| return this.htmlTagIndex != -1 && (this.htmlTagIndex & JAVADOC_CLOSED_TAG) != 0; |
| } |
| |
| /** |
| * Returns whether the text is a html tag or not. |
| * |
| * @return <code>true</code> if the node is a html tag, <code>false</code> |
| * otherwise. |
| */ |
| public boolean isHtmlTag() { |
| return this.htmlTagIndex != -1; |
| } |
| |
| /** |
| * Returns whether the node is an immutable html tag or not. |
| * <p> |
| * The text in an immutable tags is <b>never</b> formatted. |
| * </p> |
| * |
| * @return <code>true</code> if the node is an immutable tag, |
| * <code>false</code> otherwise. |
| */ |
| public boolean isImmutableHtmlTag() { |
| return this.htmlTagIndex != -1 && (this.htmlTagIndex & JAVADOC_TAGS_ID_MASK) == JAVADOC_IMMUTABLE_TAGS_ID; |
| |
| } |
| |
| /** |
| * Returns whether the text is immutable or not. |
| * <p> |
| * A text in considered as immutable when it belongs to an immutable block |
| * or when it's an immutable html tag. |
| * </p> |
| * |
| * @return <code>true</code> if the node is an immutable tag, |
| * <code>false</code> otherwise. |
| */ |
| public boolean isImmutable() { |
| return this.immutable || (this.htmlTagIndex != -1 && (this.htmlTagIndex & JAVADOC_TAGS_ID_MASK) == JAVADOC_IMMUTABLE_TAGS_ID); |
| |
| } |
| |
| /** |
| * Returns whether the text at the given separator index position is after a |
| * separator tag or not. |
| * |
| * @return <code>true</code> if the text is after a separator tag, |
| * <code>false</code> otherwise or if the given index is out the range of |
| * the text separators. |
| */ |
| public boolean isTextAfterHtmlSeparatorTag(int separatorIndex) { |
| int ptr = separatorIndex; |
| if (ptr > this.separatorsPtr) return false; |
| int tagIndex = this.htmlIndexes[ptr] & JAVADOC_TAGS_ID_MASK; |
| return tagIndex != -1 && tagIndex == JAVADOC_SEPARATOR_TAGS_ID; |
| |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.internal.formatter.FormatJavadocNode#isText() |
| */ |
| public boolean isText() { |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.internal.formatter.FormatJavadocNode#setHeaderLine(int) |
| */ |
| void setHeaderLine(int javadocLineStart) { |
| for (int i=0; i<this.htmlNodesPtr; i++) { |
| FormatJavadocNode node = this.htmlNodes[i]; |
| if (!node.isText()) { |
| ((FormatJavadocBlock) node).setHeaderLine(javadocLineStart); |
| } |
| } |
| } |
| |
| protected void toString(StringBuffer buffer) { |
| StringBuffer indentation = new StringBuffer(); |
| for (int t=0; t<=this.depth; t++) indentation.append('\t'); |
| buffer.append(indentation); |
| if (isImmutable()) { |
| buffer.append("immutable "); //$NON-NLS-1$ |
| } |
| buffer.append("text"); //$NON-NLS-1$ |
| super.toString(buffer); |
| buffer.append(" ("); //$NON-NLS-1$ |
| buffer.append(this.separatorsPtr+1).append(" sections, "); //$NON-NLS-1$ |
| buffer.append(this.htmlNodesPtr+1).append(" html tags, "); //$NON-NLS-1$ |
| buffer.append(this.depth).append(" depth, "); //$NON-NLS-1$ |
| buffer.append(this.linesBefore).append(" before, "); //$NON-NLS-1$ |
| String tagID = "no"; //$NON-NLS-1$ |
| switch (getHtmlTagID()) { |
| case JAVADOC_TAGS_ID_MASK: |
| tagID = "mask"; //$NON-NLS-1$ |
| break; |
| case JAVADOC_SINGLE_BREAK_TAG_ID: |
| tagID = "single break"; //$NON-NLS-1$ |
| break; |
| case JAVADOC_CODE_TAGS_ID: |
| tagID = "code"; //$NON-NLS-1$ |
| break; |
| case JAVADOC_BREAK_TAGS_ID: |
| tagID = "break"; //$NON-NLS-1$ |
| break; |
| case JAVADOC_IMMUTABLE_TAGS_ID: |
| tagID = "immutable"; //$NON-NLS-1$ |
| break; |
| case JAVADOC_SEPARATOR_TAGS_ID: |
| tagID = "separator"; //$NON-NLS-1$ |
| break; |
| } |
| buffer.append(tagID).append(" tag id)"); //$NON-NLS-1$ |
| buffer.append('\n'); |
| } |
| |
| public void toStringDebug(StringBuffer buffer, char[] source) { |
| if (buffer.length() > 0) { |
| for (int l=0; l<this.linesBefore; l++) { |
| buffer.append('\n'); |
| for (int t=0; t<this.depth; t++) buffer.append('\t'); |
| } |
| } |
| if (this.separatorsPtr == -1) { |
| super.toStringDebug(buffer, source); |
| return; |
| } |
| int ptr = 0; |
| int nextStart = this.sourceStart; |
| int idx = 0; |
| while (idx<=this.separatorsPtr || (this.htmlNodesPtr != -1 && ptr <= this.htmlNodesPtr)) { |
| if (idx > this.separatorsPtr) { |
| // last node |
| FormatJavadocNode node = this.htmlNodes[ptr++]; |
| node.toStringDebug(buffer, source); |
| return; |
| } |
| int end = (int) (this.separators[idx] >>> 32); |
| if (this.htmlNodesPtr >= 0 && ptr <= this.htmlNodesPtr && end > this.htmlNodes[ptr].sourceStart) { |
| FormatJavadocNode node = this.htmlNodes[ptr++]; |
| node.toStringDebug(buffer, source); |
| } else { |
| if (idx > 1 && source[nextStart] != '<') { |
| buffer.append('\n'); |
| for (int t=0; t<this.depth; t++) buffer.append('\t'); |
| } |
| buffer.append(source, nextStart, end - nextStart + 1); |
| } |
| nextStart = (int) this.separators[idx++]; |
| } |
| if (source[nextStart] == '<') { |
| switch (getHtmlTagID()) { |
| case JAVADOC_CODE_TAGS_ID: |
| buffer.append('\n'); |
| for (int t=0; t<this.depth; t++) buffer.append('\t'); |
| break; |
| } |
| } |
| buffer.append(source, nextStart, this.sourceEnd-nextStart+1); |
| buffer.append(' '); |
| } |
| } |