/**
* Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.parser.prettyprinterv2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.python.pydev.parser.jython.SimpleNode;
import org.python.pydev.parser.jython.ast.commentType;
import org.python.pydev.shared_core.string.FastStringBuffer;
/**
* Defines a line in the document. The items are added based on the order in which items are added
* and their actual columns.
*
* So, if 2 items are added to the same column, the one added 1st has precedence over the other.
*
*/
public class PrettyPrinterDocLineEntry {
/**
* Holds the line parts available.
*/
private ArrayList<ILinePart> lineParts = new ArrayList<ILinePart>();
/**
* The difference from indents/dedents in the current line (e.g.: if there are 2 indents
* and 1 dedent in this line, indentDiff = 1)
*/
private int indentDiff;
/**
* The number of empty lines required after a dedent in this line.
*/
private int emptyLinesRequiredAfterDedent;
/**
* The number of this line in the document.
*/
public final int line;
/**
* Marks if the line is currently sorted or not. Whenever the line is changed, this
* attribute has to be marked as false.
*/
private boolean lineSorted = false;
public PrettyPrinterDocLineEntry(int line) {
this.line = line;
}
private void addPart(ILinePart linePart) {
int before = -1;
if (linePart instanceof LinePartRequireMark) {
String token = ((LinePartRequireMark) linePart).getToken().trim();
if (token.equals(":") || token.equals(",") || token.equals("(") || token.equals(")") || token.equals("[")
|| token.equals("]") || token.equals("=") || token.equals("{") || token.equals("}")) {
if (lineParts.size() > 0) {
for (int i = lineParts.size() - 1; i >= 0; i--) {
ILinePart existing = lineParts.get(i);
if (existing instanceof LinePartStatementMark || existing.getToken() instanceof commentType) {
before = i;
} else {
break;
}
}
}
}
}
if (before != -1) {
lineParts.add(before, linePart);
} else {
lineParts.add(linePart);
}
lineSorted = false;
}
private void addPart(int i, ILinePart linePart) {
lineParts.add(i, linePart);
lineSorted = false;
}
private void sortLineParts() {
if (!lineSorted) {
Collections.sort(lineParts, new Comparator<ILinePart>() {
@Override
public int compare(ILinePart o1, ILinePart o2) {
return (o1.getBeginCol() < o2.getBeginCol() ? -1 : (o1.getBeginCol() == o2.getBeginCol() ? 0 : 1));
}
});
lineSorted = true;
}
}
public ILinePart add(int beginCol, String string, Object token) {
ILinePart linePart = new LinePart(beginCol, string, token, this);
addPart(linePart);
return linePart;
}
public ILinePart addBefore(int beginCol, String string, Object token) {
ILinePart linePart = new LinePart(beginCol, string, token, this);
//Now, on the start, we want to add it before any existing in the same column.
for (int i = 0; i < this.lineParts.size(); i++) {
if (beginCol == this.lineParts.get(i).getBeginCol()) {
addPart(i, linePart);
return linePart;
}
}
addPart(linePart);
return linePart;
}
@Override
public String toString() {
FastStringBuffer buf = new FastStringBuffer();
for (ILinePart c : getSortedParts()) {
if (c instanceof ILinePart2) {
ILinePart2 iLinePart2 = (ILinePart2) c;
buf.append(iLinePart2.getString());
} else {
buf.append(c.toString());
}
buf.append(c.isMarkedAsFound() ? "+" : "?");
buf.append(" ");
}
return buf.toString();
}
public List<ILinePart> getSortedParts() {
sortLineParts();
return this.lineParts;
}
public void indent(SimpleNode node) {
indent(node, false);
}
public LinePartIndentMark indent(SimpleNode node, boolean requireNewLine) {
this.indentDiff += 1;
LinePartIndentMark linePartIndentMark = new LinePartIndentMark(node.beginColumn, node, true, this);
linePartIndentMark.setRequireNewLine(requireNewLine);
addPart(linePartIndentMark);
return linePartIndentMark;
}
public LinePartIndentMark dedent(int emptyLinesRequiredAfterDedent) {
if (this.emptyLinesRequiredAfterDedent < emptyLinesRequiredAfterDedent) {
this.emptyLinesRequiredAfterDedent = emptyLinesRequiredAfterDedent;
}
this.indentDiff -= 1;
List<ILinePart> sortedParts = this.getSortedParts();
LinePartIndentMark dedentMark;
if (sortedParts.size() > 0) {
dedentMark = new LinePartIndentMark(sortedParts.get(sortedParts.size() - 1).getBeginCol(), "", false, this);
sortedParts.add(dedentMark);
} else {
dedentMark = new LinePartIndentMark(0, "", false, this);
addPart(dedentMark);
}
return dedentMark;
}
public LinePartIndentMark indentAfter(ILinePart after, boolean requireNewLine) {
this.indentDiff += 1;
LinePartIndentMark linePartIndentMark = new LinePartIndentMark(after.getBeginCol(), after.getToken(), true,
this);
linePartIndentMark.setRequireNewLine(requireNewLine);
addPart(lineParts.indexOf(after) + 1, linePartIndentMark);
return linePartIndentMark;
}
public int getIndentDiff() {
return this.indentDiff;
}
public int getFirstCol() {
sortLineParts();
if (this.lineParts.size() > 0) {
ILinePart iLinePart0 = this.lineParts.get(0);
if (!(iLinePart0 instanceof LinePartRequireAdded)) {
return iLinePart0.getBeginCol();
}
}
return -1;
}
public void addStartStatementMark(ILinePart foundWithLowerLocation, SimpleNode node) {
sortLineParts();
//Now, on the start, we want to add it before any existing in the same column.
for (int i = 0; i < this.lineParts.size(); i++) {
if (foundWithLowerLocation == this.lineParts.get(i)) {
addPart(i, new LinePartStatementMark(foundWithLowerLocation.getBeginCol(), node, true, this));
return;
}
}
addPart(new LinePartStatementMark(foundWithLowerLocation.getBeginCol(), node, true, this));
}
public void addEndStatementMark(ILinePart foundWithHigherLocation, SimpleNode node) {
addPart(new LinePartStatementMark(foundWithHigherLocation.getBeginCol(), node, false, this));
}
public int getNewLinesRequired() {
return this.emptyLinesRequiredAfterDedent;
}
public LinePartRequireMark addRequireMark(int beginColumn, String string) {
LinePartRequireMark mark = new LinePartRequireMark(beginColumn, string, this);
addPart(mark);
return mark;
}
public LinePartRequireMark addRequireMark(int beginColumn, String... string) {
LinePartRequireMark mark = new LinePartRequireMark(beginColumn, this, string);
addPart(mark);
return mark;
}
public LinePartRequireIndentMark addRequireIndentMark(int beginColumn, String string) {
LinePartRequireIndentMark ret = new LinePartRequireIndentMark(beginColumn, string, this);
addPart(ret);
return ret;
}
public LinePartRequireMark addRequireMarkBefore(ILinePart o1, String string) {
LinePartRequireMark linePart = new LinePartRequireMark(o1.getBeginCol(), string, this);
for (int i = 0; i < this.lineParts.size(); i++) {
if (o1 == this.lineParts.get(i)) {
addPart(i, linePart);
return linePart;
}
}
addPart(linePart);
return linePart;
}
public LinePartRequireMark addRequireMarkAfterBefore(ILinePart o1, String string) {
LinePartRequireMark linePart = new LinePartRequireMark(o1.getBeginCol(), string, this);
for (int i = 0; i < this.lineParts.size(); i++) {
if (o1 == this.lineParts.get(i)) {
addPart(i + 1, linePart);
return linePart;
}
}
addPart(linePart);
return linePart;
}
}