/** * Copyright (c) 2005-2011 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.core.docutils; import java.util.Iterator; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; public class PyDocIterator implements Iterator<String> { private int offset; private IDocument doc; private boolean addNewLinesToRet = true; private boolean returnNewLinesOnLiterals = false; private boolean inLiteral = false; private int literalEnd = 0; private boolean changeLiteralsForSpaces = false; private int lastReturned = -1; private boolean addComments = false; public PyDocIterator(IDocument doc, boolean addNewLinesToRet) { this(doc, addNewLinesToRet, false, false); } public PyDocIterator(IDocument doc, boolean addNewLinesToRet, boolean returnNewLinesOnLiterals, boolean changeLiteralsForSpaces) { this(doc, addNewLinesToRet, returnNewLinesOnLiterals, changeLiteralsForSpaces, false); } /** * @param doc the document where we will iterate * @param addNewLinesToRet whether the new line character should be added to the return * @param returnNewLinesOnLiterals whether we should return the new lines found in the literals (not the char, but the line itself) * @param changeLiteralsForSpaces whether we should replace the literals with spaces (so that we don't loose offset information) * @param addComments if true, comments found will be yielded (otherwise, no comments will be shown) */ public PyDocIterator(IDocument doc, boolean addNewLinesToRet, boolean returnNewLinesOnLiterals, boolean changeLiteralsForSpaces, boolean addComments) { this(doc); this.addNewLinesToRet = addNewLinesToRet; this.returnNewLinesOnLiterals = returnNewLinesOnLiterals; this.changeLiteralsForSpaces = changeLiteralsForSpaces; this.addComments = addComments; } public PyDocIterator(IDocument doc) { this.doc = doc; } /** * Changes the current offset in the document. Note: this method is not safe for use after the iteration * started! * * @param offset the offset where this class should start parsing (note: the offset must be a * code partition, otherwise the yielded values will be wrong). */ public void setStartingOffset(int offset) { this.offset = offset; } public boolean hasNext() { return offset < doc.getLength(); } public int getLastReturnedLine() { try { lastReturned = doc.getLineOfOffset(offset - 1); } catch (BadLocationException e) { //ignore (keep the last one) } return lastReturned; } private String nextInLiteral() { StringBuffer buf = new StringBuffer(); try { char ch = doc.getChar(offset); while (offset < literalEnd && ch != '\n' && ch != '\r') { ch = doc.getChar(offset); offset++; if (changeLiteralsForSpaces && ch != '\n' && ch != '\r') { buf.append(' '); } } if (offset >= literalEnd) { inLiteral = false; offset++; if (changeLiteralsForSpaces) { buf.append(' '); } return buf.toString(); } if (ch == '\r') { ch = doc.getChar(offset + 1); if (ch == '\n') { offset++; ch = '\n'; } } buf.append(ch); return buf.toString(); } catch (Exception e) { throw new RuntimeException(e); } } /** * @return the next line in the document */ public String next() { try { StringBuffer buf = new StringBuffer(); if (inLiteral) { int initialOffset = offset; String ret = nextInLiteral(); if (ret.length() > 0 && initialOffset < offset) { //if it didn't move in the offset, disregard the results if (StringUtils.endsWith(ret, '\r') || StringUtils.endsWith(ret, '\n')) { if (!addNewLinesToRet) { ret = ret.substring(0, ret.length() - 1); } buf.append(ret); return ret; } else { buf.append(ret); } } } char ch = 0; int docLen = doc.getLength(); ParsingUtils parsingUtils = ParsingUtils.create(doc); while (ch != '\r' && ch != '\n' && offset < docLen) { ch = doc.getChar(offset); if (ch == '#') { while (offset < docLen && ch != '\n' && ch != '\r') { ch = doc.getChar(offset); if (addComments && ch != '\n' && ch != '\r') { buf.append(ch); } offset++; } } else if (ch == '\'' || ch == '"') { if (returnNewLinesOnLiterals) { inLiteral = true; literalEnd = parsingUtils.getLiteralEnd(offset, ch); String ret = nextInLiteral(); if (ret.length() > 0) { if (StringUtils.endsWith(ret, '\r') || StringUtils.endsWith(ret, '\n')) { if (!addNewLinesToRet) { ret = ret.substring(0, ret.length() - 1); } buf.append(ret); return buf.toString(); } else { buf.append(ret); } } } else { if (this.changeLiteralsForSpaces) { throw new RuntimeException("Not supported in this case."); } offset = parsingUtils.getLiteralEnd(offset, ch); offset++; } } else if (ch != '\n' && ch != '\r') { //will be added later buf.append(ch); offset++; } else { offset++; } } //handle the \r, \n or \r\n if (ch == '\n' || ch == '\r') { if (addNewLinesToRet) { buf.append(ch); } if (ch == '\r') { if (offset < docLen && doc.getChar(offset) == '\n') { offset++; if (addNewLinesToRet) { buf.append('\n'); } } } } return buf.toString(); } catch (Exception e) { throw new RuntimeException(e); } } public void remove() { throw new RuntimeException("Not Impl."); } }