|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor; import javax.swing.text.AbstractDocument; import javax.swing.text.Element; import javax.swing.text.Document; import javax.swing.text.AttributeSet; import javax.swing.text.Position; import javax.swing.undo.UndoableEdit; import java.util.ArrayList; import javax.swing.text.BadLocationException; import javax.swing.text.Segment; import javax.swing.text.StyleContext; import org.netbeans.modules.editor.util.element.GapBranchElement; import org.openide.ErrorManager; /** * Line root element implementation. * * @author Miloslav Metelka * @version 1.00 */ final class LineRootElement extends GapBranchElement { private static final LineElement[] EMPTY_LINE_ELEMENT_ARRAY = new LineElement[0]; private static final String NAME = AbstractDocument.SectionElementName; private BaseDocument doc; private LineElement[] addedLines = EMPTY_LINE_ELEMENT_ARRAY; LineRootElement(BaseDocument doc) { this.doc = doc; assert (doc.getLength() == 0) : "Cannot start with non-empty document"; // NOI18N Position startPos = doc.getStartPosition(); assert (startPos.getOffset() == 0) : "Document.getStartPosition() != 0"; // NOI18N Position endPos = doc.getEndPosition(); assert (endPos.getOffset() == 1) : "Document.getEndPosition() != 1"; // NOI18N Element line = new LineElement(this, startPos, endPos); replace(0, 0, new Element[]{ line }); assert (getElement(0) != null); } /** * Double size of the addedLines array and return * the index value that corresponds to the original zero index. */ private int doubleAddedLinesCapacity() { int addedLinesLength = addedLines.length; int newCapacity = Math.max(4, addedLinesLength * 2); LineElement[] newAddedLines = new LineElement[newCapacity]; // Copy current contents to end of array System.arraycopy(addedLines, 0, newAddedLines, newCapacity - addedLinesLength, addedLinesLength); addedLines = newAddedLines; return (newCapacity - addedLinesLength); // value for original index zero } public Element getElement(int index) { if (index < 0) { throw new IndexOutOfBoundsException("Invalid line index=" + index + " < 0"); // NOI18N } int elementCount = getElementCount(); if (index >= elementCount) { throw new IndexOutOfBoundsException("Invalid line index=" + index // NOI18N + " >= lineCount=" + elementCount); // NOI18N } LineElement elem = (LineElement)super.getElement(index); assert (elem != null); return elem; } UndoableEdit insertUpdate(int insertOffset, int insertLength) { int lastInsertedCharOffset = insertOffset + insertLength - 1; CharSeq text = doc.getText(); Edit edit = null; int index = -1; // Index of the elements modification Element[] removeElements = null; // Removed line elements // Index in the addedLines array - adding from last to first int firstAddedLineIndex = addedLines.length; // nothing added yet int offset = lastInsertedCharOffset; // insertAtPrevLineEndOffset - whether the insert was done at the end (after '\n') // of a previous line i.e. in fact at a begining of the next line. boolean insertAtPrevLineEndOffset; int beforeInsertOffset; // in fact Math.max(insertOffset - 1, 0) if (insertOffset == 0) { // [swing] marks (and elements) at offset zero do not move up beforeInsertOffset = 0; insertAtPrevLineEndOffset = false; } else { // inserting inside doc beforeInsertOffset = insertOffset - 1; // check char before offset for '\n' insertAtPrevLineEndOffset = (text.charAt(beforeInsertOffset) == '\n'); } try { // Go through all the inserted lines plus the char at beforeInsertOffset (if exists) // and create new line elements at every occurrence of '\n' Position futureAddedLineEndPos = null; while (offset >= beforeInsertOffset) { if (text.charAt(offset) == '\n') { // line break at offset boolean addLine = true; // whether line element should be added if (futureAddedLineEndPos == null) { // Find the first line element that will be removed index = getElementIndex(insertOffset); LineElement removeLine = (LineElement)getElement(index); // If inserting at begining of line (insertAtPrevLineEndOffset == true) // and the inserted chars do not end with '\n' // then not only current line must be removed // but the next one as well. if (insertAtPrevLineEndOffset) { // '\n' at (insertOffset - 1) if (offset == lastInsertedCharOffset) { // inserted 'xxx\n' removeElements = new Element[] { removeLine }; futureAddedLineEndPos = removeLine.getEndPosition(); addLine = false; // do not add new line in this case } else { LineElement nextRemoveLine = (LineElement)getElement(index + 1); removeElements = new Element[] { removeLine, nextRemoveLine }; futureAddedLineEndPos = nextRemoveLine.getEndPosition(); } } else { // otherwise use the next element as the next for added removeElements = new Element[] { removeLine }; futureAddedLineEndPos = removeLine.getEndPosition(); } } if (addLine) { if (firstAddedLineIndex == 0) { // no more space to add firstAddedLineIndex = doubleAddedLinesCapacity(); } firstAddedLineIndex--; // will fill in added line element soon Position lineStartPos = doc.createPosition(offset + 1); addedLines[firstAddedLineIndex] = new LineElement( this, lineStartPos, futureAddedLineEndPos); futureAddedLineEndPos = lineStartPos; } } offset--; } if (futureAddedLineEndPos != null) { // will add (and remove) lines // Create array of added lines and add extra one at begining int addedLineCount = addedLines.length - firstAddedLineIndex; Element[] addElements = new Element[addedLineCount + 1]; System.arraycopy(addedLines, firstAddedLineIndex, addElements, 1, addedLineCount); addElements[0] = new LineElement( this, ((LineElement)removeElements[0]).getStartPosition(), futureAddedLineEndPos ); replace(index, removeElements.length, addElements); edit = new Edit(index, removeElements, addElements); } } catch (BadLocationException e) { // Should never happen but in case it happens // retain the current consistent state (no replace is done) // and report this as serious error ErrorManager.getDefault().notify(ErrorManager.ERROR, e); } checkConsistency(); return edit; } UndoableEdit removeUpdate(int removeOffset, int removeLength) { // The algorithm here is similar to the one in PlainDocument.removeUpdate(). // Unfortunately in case exactly a line element (or multiple line elements) // the algorithm removes extra line that follows the end of removed area. // That could be improved but compatibility with PlainDocument would be lost. Edit edit = null; int removeEndOffset = removeOffset + removeLength; int line0 = getElementIndex(removeOffset); int line1 = getElementIndex(removeEndOffset); if (line0 != line1) { // at least one line was removed line1++; // will remove the line where remove ends as well Element[] removeElements = new Element[line1 - line0]; copyElements(line0, line1, removeElements, 0); Element[] addElements = new Element[] { new LineElement(this, ((LineElement)removeElements[0]).getStartPosition(), ((LineElement)removeElements[removeElements.length - 1]).getEndPosition() ) }; replace(line0, removeElements.length, addElements); edit = new Edit(line0, removeElements, addElements); } checkConsistency(); return edit; } /* protected void compact() { super.compact(); addedLines = EMPTY_LINE_ELEMENT_ARRAY; } */ public Document getDocument() { return doc; } public Element getParentElement() { return null; } public String getName() { return NAME; } public AttributeSet getAttributes() { return StyleContext.getDefaultStyleContext().getEmptySet(); } public int getStartOffset() { return 0; } public int getEndOffset() { return doc.getLength() + 1; } public int getElementIndex(int offset) { if (offset == 0) { // NB uses this frequently to just get the parent return 0; } return super.getElementIndex(offset); } private void checkConsistency() { int lineCount = getElementCount(); assert (lineCount > 0); // Should be 1 or greater int prevLineEndOffset = 0; for (int i = 0; i < lineCount; i++) { LineElement elem = (LineElement)getElement(i); assert (prevLineEndOffset == elem.getStartOffset()); assert (prevLineEndOffset < elem.getEndOffset()) : "Line " + i + " of " + lineCount + ": " + lineToString(elem); // NOI18N prevLineEndOffset = elem.getEndOffset(); } assert (prevLineEndOffset == (doc.getLength() + 1)); } private String lineToString(Element line) { return "<" + line.getStartOffset() + ", " // NOI18N + line.getEndOffset() + ">"; // NOI18N } } |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.