Package org.aspectj.org.eclipse.jdt.internal.formatter.comment

Source Code of org.aspectj.org.eclipse.jdt.internal.formatter.comment.CommentRegion

/*******************************************************************************
* Copyright (c) 2000, 2007 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.aspectj.org.eclipse.jdt.internal.formatter.comment;

import java.util.Iterator;
import java.util.LinkedList;

import org.eclipse.core.runtime.Assert;

import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ILineTracker;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;

import org.aspectj.org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
import org.aspectj.org.eclipse.jdt.internal.formatter.CodeFormatterVisitor;
import org.aspectj.org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
import org.aspectj.org.eclipse.jdt.internal.formatter.Scribe;

/**
* Comment region in a source code document.
*
* @since 3.0
*/
public class CommentRegion extends Position implements IHtmlTagDelimiters, IBorderAttributes, ICommentAttributes {

  /** Default comment range delimiter */
  protected static final String COMMENT_RANGE_DELIMITER= " "; //$NON-NLS-1$

  /** Default line prefix length */
  private static final int COMMENT_PREFIX_LENGTH= 3;

  /** The borders of this region */
  private int fBorders= 0;

  /** Should all blank lines be cleared during formatting? */
  protected boolean fClear;

  /** The line delimiter used in this comment region */
  private final String fDelimiter;

  /** The document to format */
  private final IDocument fDocument;

  /** The lines in this comment region */
  private final LinkedList fLines= new LinkedList();
 
  /** The formatting preferences */
  protected final DefaultCodeFormatterOptions preferences;
 
  /** The comment ranges in this comment region */
  private final LinkedList fRanges= new LinkedList();

  /** Is this comment region a single line region? */
  private final boolean fSingleLine;

  /** Number of spaces representing tabulator */
  private int fTabSize;

  /** the scribe used to create edits */
  protected Scribe scribe;

  /**
   * Creates a new comment region.
   *
   * @param document the document which contains the comment region
   * @param position the position of this comment region in the document
   * @param formatter the given code formatter
   */
  public CommentRegion(final IDocument document, final Position position, final CodeFormatterVisitor formatter) {
    super(position.getOffset(), position.getLength());

    this.preferences = formatter.preferences;
    fDelimiter = this.preferences.line_separator;
    fDocument= document;
   
    fTabSize= DefaultCodeFormatterOptions.SPACE == this.preferences.tab_char ? this.preferences.indentation_size : this.preferences.tab_size;

    this.scribe = formatter.scribe;

    final ILineTracker tracker= new DefaultLineTracker();

    IRegion range= null;
    CommentLine line= null;

    tracker.set(getText(0, getLength()));
    final int lines= tracker.getNumberOfLines();

    fSingleLine= lines == 1;

    try {

      for (int index= 0; index < lines; index++) {

        range= tracker.getLineInformation(index);
        line= createLine();
        line.append(new CommentRange(range.getOffset(), range.getLength()));

        fLines.add(line);
      }

    } catch (BadLocationException exception) {
      // Should not happen
    }
  }

  /**
   * Appends the comment range to this comment region.
   *
   * @param range comment range to append to this comment region
   */
  protected final void append(final CommentRange range) {
    fRanges.addLast(range);
  }

  /**
   * Can the comment range be appended to the comment line?
   *
   * @param line comment line where to append the comment range
   * @param previous comment range which is the predecessor of the current
   *                comment range
   * @param next comment range to test whether it can be appended to the
   *                comment line
   * @param index amount of space in the comment line used by already
   *                inserted comment ranges
   * @param width the maximal width of text in this comment region
   *                measured in average character widths
   * @return <code>true</code> iff the comment range can be added to the
   *         line, <code>false</code> otherwise
   */
  protected boolean canAppend(final CommentLine line, final CommentRange previous, final CommentRange next, final int index, final int width) {
    return index == 0 || index + next.getLength() <= width;
  }

  /**
   * Can the whitespace between the two comment ranges be formatted?
   *
   * @param previous previous comment range which was already formatted,
   *                can be <code>null</code>
   * @param next next comment range to be formatted
   * @return <code>true</code> iff the next comment range can be
   *         formatted, <code>false</code> otherwise.
   */
  protected boolean canFormat(final CommentRange previous, final CommentRange next) {
    return previous != null;
  }

  /**
   * Formats the comment region with the given indentation level.
   *
   * @param indentationLevel the indentation level
   * @return the resulting text edit of the formatting process
   * @since 3.1
   */
  public final TextEdit format(int indentationLevel, boolean returnEdit) {
    final String probe= getText(0, CommentLine.NON_FORMAT_START_PREFIX.length());
    if (!probe.startsWith(CommentLine.NON_FORMAT_START_PREFIX)) {

      int margin= this.preferences.comment_line_length;
      String indentation= computeIndentation(indentationLevel);
      margin= Math.max(COMMENT_PREFIX_LENGTH + 1, margin - stringToLength(indentation) - COMMENT_PREFIX_LENGTH);

      tokenizeRegion();
      markRegion();
      wrapRegion(margin);
      formatRegion(indentation, margin);

    }
    if (returnEdit) {
      return this.scribe.getRootEdit();
    }
    return null;
  }

  /**
   * Formats this comment region.
   *
   * @param indentation the indentation of this comment region
   * @param width the maximal width of text in this comment region
   *                measured in average character widths
   */
  protected void formatRegion(final String indentation, final int width) {

    final int last= fLines.size() - 1;
    if (last >= 0) {

      CommentLine lastLine= (CommentLine)fLines.get(last);
      CommentRange lastRange= lastLine.getLast();
      lastLine.formatLowerBorder(lastRange, indentation, width);

      CommentLine previous;
      CommentLine next= null;
      CommentRange range= null;
      for (int line= last; line >= 0; line--) {

        previous= next;
        next= (CommentLine)fLines.get(line);

        range= next.formatLine(previous, range, indentation, line);
      }
      next.formatUpperBorder(range, indentation, width);
    }
  }

  /**
   * Returns the line delimiter used in this comment region.
   *
   * @return the line delimiter for this comment region
   */
  protected final String getDelimiter() {
    return fDelimiter;
  }

  /**
   * Returns the line delimiter used in this comment line break.
   *
   * @param predecessor the predecessor comment line after the line break
   * @param successor the successor comment line before the line break
   * @param previous the comment range after the line break
   * @param next the comment range before the line break
   * @param indentation indentation of the formatted line break
   * @return the line delimiter for this comment line break
   */
  protected String getDelimiter(final CommentLine predecessor, final CommentLine successor, final CommentRange previous, final CommentRange next, final String indentation) {
    return fDelimiter + indentation + successor.getContentPrefix();
  }

  /**
   * Returns the range delimiter for this comment range break.
   *
   * @param previous the previous comment range to the right of the range
   *                delimiter
   * @param next the next comment range to the left of the range delimiter
   * @return the delimiter for this comment range break
   */
  protected String getDelimiter(final CommentRange previous, final CommentRange next) {
    return COMMENT_RANGE_DELIMITER;
  }

  /**
   * Returns the document of this comment region.
   *
   * @return the document of this region
   */
  protected final IDocument getDocument() {
    return fDocument;
  }

  /**
   * Returns the comment ranges in this comment region
   *
   * @return the comment ranges in this region
   */
  protected final LinkedList getRanges() {
    return fRanges;
  }

  /**
   * Returns the number of comment lines in this comment region.
   *
   * @return the number of lines in this comment region
   */
  protected final int getSize() {
    return fLines.size();
  }

  /**
   * Returns the text of this comment region in the indicated range.
   *
   * @param position the offset of the comment range to retrieve in
   *                comment region coordinates
   * @param count the length of the comment range to retrieve
   * @return the content of this comment region in the indicated range
   */
  protected final String getText(final int position, final int count) {

    String content= ""; //$NON-NLS-1$
    try {
      content= fDocument.get(getOffset() + position, count);
    } catch (BadLocationException exception) {
      // Should not happen
    }
    return content;
  }

  /**
   * Does the border <code>border</code> exist?
   *
   * @param border the type of the border, must be a border attribute of
   *                <code>CommentRegion</code>
   * @return <code>true</code> iff this border exists,
   *         <code>false</code> otherwise
   */
  protected final boolean hasBorder(final int border) {
    return (fBorders & border) == border;
  }

  /**
   * Does the comment range consist of letters and digits only?
   *
   * @param range the comment range to text
   * @return <code>true</code> iff the comment range consists of letters
   *         and digits only, <code>false</code> otherwise
   */
  protected final boolean isAlphaNumeric(final CommentRange range) {

    final String token= getText(range.getOffset(), range.getLength());

    for (int index= 0; index < token.length(); index++) {
      if (!ScannerHelper.isLetterOrDigit(token.charAt(index)))
        return false;
    }
    return true;
  }

  /**
   * Does the comment range contain no letters and digits?
   *
   * @param range the comment range to text
   * @return <code>true</code> iff the comment range contains no letters
   *         and digits, <code>false</code> otherwise
   */
  protected final boolean isNonAlphaNumeric(final CommentRange range) {

    final String token= getText(range.getOffset(), range.getLength());

    for (int index= 0; index < token.length(); index++) {
      if (ScannerHelper.isLetterOrDigit(token.charAt(index)))
        return false;
    }
    return true;
  }

  /**
   * Should blank lines be cleared during formatting?
   *
   * @return <code>true</code> iff blank lines should be cleared,
   *         <code>false</code> otherwise
   */
  protected final boolean isClearLines() {
    return fClear;
  }

  /**
   * Is this comment region a single line region?
   *
   * @return <code>true</code> iff this region is single line,
   *         <code>false</code> otherwise
   */
  protected final boolean isSingleLine() {
    return fSingleLine;
  }

  /**
   * Logs a text edit operation occurred during the formatting process
   *
   * @param change the changed text
   * @param position offset measured in comment region coordinates where
   *                to apply the changed text
   * @param count length of the range where to apply the changed text
   */
  protected final void logEdit(final String change, final int position, final int count) {
    try {
      final int base= getOffset() + position;
      final String content= fDocument.get(base, count);

      if (!change.equals(content)) {
        if (count > 0) {
          this.scribe.addReplaceEdit(base, base + count - 1, change);
        } else {
          this.scribe.addInsertEdit(base, change);
        }
      }
    } catch (BadLocationException exception) {
      // Should not happen
      CommentFormatterUtil.log(exception);
    } catch (MalformedTreeException exception) {
      // Do nothing
      CommentFormatterUtil.log(exception);
    }
  }

  /**
   * Marks the comment ranges in this comment region.
   */
  protected void markRegion() {
    // Do nothing
  }

  /**
   * Set the border type <code>border</code> to true.
   *
   * @param border the type of the border. Must be a border attribute of
   *                <code>CommentRegion</code>
   */
  protected final void setBorder(final int border) {
    fBorders |= border;
  }

  /**
   * Returns the indentation of the given indentation level.
   *
   * @param indentationLevel the indentation level
   * @return the indentation of the given indentation level
   * @since 3.1
   */
  private String computeIndentation(int indentationLevel) {
    if (DefaultCodeFormatterOptions.TAB == this.preferences.tab_char)
      return replicate("\t", indentationLevel); //$NON-NLS-1$

    if (DefaultCodeFormatterOptions.SPACE == this.preferences.tab_char)
      return replicate(" ", indentationLevel * this.preferences.tab_size); //$NON-NLS-1$
   
    if (DefaultCodeFormatterOptions.MIXED == this.preferences.tab_char) {
      int tabSize= this.preferences.tab_size;
      int indentSize= this.preferences.indentation_size;
      int spaceEquivalents= indentationLevel * indentSize;
      return replicate("\t", spaceEquivalents / tabSize) + replicate(" ", spaceEquivalents % tabSize); //$NON-NLS-1$ //$NON-NLS-2$
    }
   
    Assert.isTrue(false);
    return null;
  }
 
  /**
   * Returns the given string n-times replicated.
   *
   * @param string the string
   * @param n n
   * @return the given string n-times replicated
   * @since 3.1
   */
  private String replicate(String string, int n) {
    StringBuffer buffer= new StringBuffer(n*string.length());
    for (int i= 0; i < n; i++)
      buffer.append(string);
    return buffer.toString();
  }

  /**
   * Computes the equivalent indentation for a string
   *
   * @param reference the string to compute the indentation for
   * @return the indentation string
   */
  protected final String stringToIndent(final String reference) {
    return replicate(" ", stringToLength(reference)); //$NON-NLS-1$
  }

  /**
   * Returns the length of the string in expanded characters.
   *
   * @param reference the string to get the length for
   * @return the length of the string in expanded characters
   */
  protected final int stringToLength(final String reference) {
    return expandTabs(reference).length();
  }

  /**
   * Expands the given string's tabs according to the given tab size.
   *
   * @param string the string
   * @return the expanded string
   * @since 3.1
   */
  private String expandTabs(String string) {
    StringBuffer expanded= new StringBuffer();
    for (int i= 0, n= string.length(), chars= 0; i < n; i++) {
      char ch= string.charAt(i);
      if (ch == '\t') {
        for (; chars < fTabSize; chars++)
          expanded.append(' ');
        chars= 0;
      } else {
        expanded.append(ch);
        chars++;
        if (chars >= fTabSize)
          chars= 0;
      }
   
    }
    return expanded.toString();
  }

  /**
   * Tokenizes the comment region.
   */
  protected void tokenizeRegion() {

    int index= 0;
    CommentLine line= null;

    for (final Iterator iterator= fLines.iterator(); iterator.hasNext(); index++) {

      line= (CommentLine)iterator.next();

      line.scanLine(index);
      line.tokenizeLine(index);
    }
  }

  /**
   * Wraps the comment ranges in this comment region into comment lines.
   *
   * @param width the maximal width of text in this comment region
   *                measured in average character widths
   */
  protected void wrapRegion(final int width) {

    fLines.clear();

    int index= 0;
    boolean adapted= false;

    CommentLine successor= null;
    CommentLine predecessor= null;

    CommentRange previous= null;
    CommentRange next= null;

    while (!fRanges.isEmpty()) {

      index= 0;
      adapted= false;

      predecessor= successor;
      successor= createLine();
      fLines.add(successor);

      while (!fRanges.isEmpty()) {
        next= (CommentRange)fRanges.getFirst();

        if (canAppend(successor, previous, next, index, width)) {

          if (!adapted && predecessor != null) {

            successor.adapt(predecessor);
            adapted= true;
          }

          fRanges.removeFirst();
          successor.append(next);

          index += (next.getLength() + 1);
          previous= next;
        } else
          break;
      }
    }
  }

  /**
   * Creates a new line for this region.
   *
   * @return a new line for this region
   * @since 3.1
   */
  protected CommentLine createLine() {
    return new SingleCommentLine(this);
  }
}
TOP

Related Classes of org.aspectj.org.eclipse.jdt.internal.formatter.comment.CommentRegion

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.