001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Alexey A. Ivanov
019: * @version $Revision$
020: */package javax.swing.text;
021:
022: import java.util.ArrayList;
023: import java.util.List;
024:
025: import org.apache.harmony.awt.text.PropertyNames;
026:
027: public class PlainDocument extends AbstractDocument {
028: public static final String lineLimitAttribute = "lineLimit";
029:
030: public static final String tabSizeAttribute = "tabSize";
031:
032: private static final int DEFAULT_TABSIZE = 8;
033:
034: // Default root element. Stores line map.
035: private BranchElement defRoot;
036:
037: public PlainDocument() {
038: this (new GapContent());
039: }
040:
041: public PlainDocument(final Content content) {
042: super (content);
043: putProperty(tabSizeAttribute, new Integer(DEFAULT_TABSIZE));
044: defRoot = (BranchElement) createDefaultRoot();
045: }
046:
047: public Element getDefaultRootElement() {
048: return defRoot;
049: }
050:
051: /**
052: * Shortcut method to get element from default root.
053: */
054: public Element getParagraphElement(final int offset) {
055: return defRoot.getElement(defRoot.getElementIndex(offset));
056: }
057:
058: /**
059: * It is expected that the result is of type
060: * <code>AbstractDocument.BranchElement</code>. If this is not the case,
061: * the constructor will fail, the exception being thrown
062: * <code>ClassCastException</code>.
063: */
064: protected AbstractElement createDefaultRoot() {
065: BranchElement root = (BranchElement) createBranchElement(null,
066: null);
067: root.replace(0, 0, new Element[] { createLeafElement(root,
068: null, getStartPosition().getOffset(), getEndPosition()
069: .getOffset()) });
070: return root;
071: }
072:
073: protected void insertUpdate(final DefaultDocumentEvent event,
074: final AttributeSet attrs) {
075: List lines = null;
076: String text = null;
077: int offset = event.getOffset();
078: int length = event.getLength();
079:
080: try {
081: text = getText(offset, length);
082: } catch (final BadLocationException e) {
083: }
084:
085: boolean hasLineBreak = text.indexOf('\n') != -1;
086: boolean prevCharIsLineBreak = false;
087: try {
088: prevCharIsLineBreak = offset > 0
089: && getText(offset - 1, 1).charAt(0) == '\n';
090: } catch (final BadLocationException e) {
091: }
092: boolean lastCharIsLineBreak = text.charAt(text.length() - 1) == '\n';
093:
094: int lineIndex;
095: int lineStart;
096: int lineEnd;
097:
098: if (prevCharIsLineBreak && !lastCharIsLineBreak) {
099: // If the inserted text does not end with new line char
100: // and at the same time the previous character in the document
101: // is new line char, we need to remove two paragraphs:
102: // the one just before the insertion point (inserted text will
103: // join it) and the following
104:
105: lineIndex = defRoot.getElementIndex(offset - 1);
106: Element prevLine = defRoot.getElement(lineIndex);
107: Element nextLine = defRoot.getElement(lineIndex + 1);
108:
109: lineStart = prevLine.getStartOffset();
110: lineEnd = nextLine.getEndOffset();
111:
112: lines = new ArrayList();
113: try {
114: breakLines(getText(lineStart, lineEnd - lineStart),
115: lineStart, lines);
116: } catch (final BadLocationException e) {
117: }
118:
119: Element[] lineArray = (Element[]) lines
120: .toArray(new Element[lines.size()]);
121:
122: updateStructure(event, lineIndex, new Element[] { prevLine,
123: nextLine }, lineArray);
124: } else if (hasLineBreak) {
125: // Paragraph structure is changed
126: lineIndex = defRoot.getElementIndex(offset);
127: Element line = defRoot.getElement(lineIndex);
128: lineStart = line.getStartOffset();
129: lineEnd = line.getEndOffset();
130:
131: lines = new ArrayList();
132: try {
133: breakLines(getText(lineStart, lineEnd - lineStart),
134: lineStart, lines);
135: } catch (final BadLocationException e) {
136: }
137:
138: Element[] lineArray = (Element[]) lines
139: .toArray(new Element[lines.size()]);
140: updateStructure(event, lineIndex, new Element[] { line },
141: lineArray);
142: }
143:
144: super .insertUpdate(event, attrs);
145: }
146:
147: protected void removeUpdate(final DefaultDocumentEvent event) {
148: String text = null;
149: int offset = event.getOffset();
150: int length = event.getLength();
151:
152: try {
153: text = getText(offset, length);
154: } catch (final BadLocationException e) {
155: return;
156: }
157:
158: boolean hasLineBreak = text.indexOf('\n') != -1;
159:
160: if (hasLineBreak) {
161: // Paragraph structure is changed
162: int index1 = defRoot.getElementIndex(offset);
163: int index2 = defRoot.getElementIndex(offset + length);
164: Element line1 = defRoot.getElement(index1);
165: Element line2 = defRoot.getElement(index2);
166:
167: int count = index2 - index1 + 1;
168: Element[] removed = new Element[count];
169: System.arraycopy(defRoot.elements, index1, removed, 0,
170: count);
171:
172: updateStructure(event, index1, removed,
173: new Element[] { createLeafElement(defRoot, null,
174: line1.getStartOffset(), line2
175: .getEndOffset()) });
176: }
177:
178: super .removeUpdate(event);
179: }
180:
181: final void doInsert(final int offset, final String text,
182: final AttributeSet attrs) throws BadLocationException {
183: super .doInsert(offset, getFilterNewLines() ? text.replace('\n',
184: ' ') : text, attrs);
185: }
186:
187: private void breakLines(final String text, final int start,
188: final List lines) {
189: final int length = text.length();
190:
191: int prevLineBreak = 0;
192: int lineBreak = text.indexOf('\n');
193: do {
194: ++lineBreak;
195:
196: lines.add(createLeafElement(defRoot, null, start
197: + prevLineBreak, start + lineBreak));
198: prevLineBreak = lineBreak;
199: lineBreak = text.indexOf('\n', prevLineBreak);
200: } while (prevLineBreak < length);
201: }
202:
203: private boolean getFilterNewLines() {
204: Object value = getProperty(PropertyNames.FILTER_NEW_LINES);
205: if (value != null) {
206: return ((Boolean) value).booleanValue();
207: }
208: return false;
209: }
210:
211: private void updateStructure(final DefaultDocumentEvent event,
212: final int index, final Element[] removed,
213: final Element[] added) {
214: event.addEdit(new ElementEdit(defRoot, index, removed, added));
215: defRoot.replace(index, removed.length, added);
216: }
217:
218: }
|