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.List;
023: import javax.swing.event.DocumentEvent.EventType;
024: import javax.swing.text.AbstractDocument.AbstractElement;
025: import javax.swing.text.AbstractDocument.BranchElement;
026: import javax.swing.text.AbstractDocument.Content;
027: import javax.swing.text.AbstractDocument.DefaultDocumentEvent;
028: import javax.swing.text.DefaultStyledDocument.ElementBuffer;
029: import junit.framework.TestCase;
030:
031: /**
032: * Tests the behavior of
033: * <code>DefaultStyledDocument.ElementBuffer.remove()</code> method.
034: *
035: */
036: public class DefaultStyledDocument_ElementBuffer_RemoveTest extends
037: TestCase {
038: private DefaultStyledDocument doc;
039:
040: private ElementBuffer buf;
041:
042: private Element root;
043:
044: private Element paragraph;
045:
046: private Content content;
047:
048: private DefaultDocumentEvent event;
049:
050: private static final AttributeSet bold = DefStyledDoc_Helpers.bold;
051:
052: private static final AttributeSet italic = DefStyledDoc_Helpers.italic;
053:
054: private static final AttributeSet boldFalse;
055:
056: private static final AttributeSet italicFalse;
057: static {
058: MutableAttributeSet mas = new SimpleAttributeSet(bold);
059: StyleConstants.setBold(mas, false);
060: boldFalse = mas;
061: mas = new SimpleAttributeSet(italic);
062: StyleConstants.setItalic(mas, false);
063: italicFalse = mas;
064: }
065:
066: @Override
067: protected void setUp() throws Exception {
068: super .setUp();
069: doc = new DefaultStyledDocument();
070: root = doc.getDefaultRootElement();
071: buf = new DefStyledDoc_Helpers.ElementBufferWithLogging(doc,
072: root);
073: doc.buffer = buf;
074: paragraph = root.getElement(0);
075: content = doc.getContent();
076: content.insertString(0, "plainbolditalic\ntext");
077: // Create the structure equivalent to this sequence:
078: //doc.insertString(doc.getLength(), "plain", null); // 5 chars
079: //doc.insertString(doc.getLength(), "bold", bold); // 4 chars
080: //doc.insertString(doc.getLength(), "italic", italic); // 6 chars
081: //doc.insertString(doc.getLength(), "\ntext", null); // 5 chars
082: doc.writeLock(); // Write lock needed to modify document structure
083: Element[] leaves = new Element[4];
084: leaves[0] = doc.createLeafElement(paragraph, null, 0, 5);
085: leaves[1] = doc.createLeafElement(paragraph, bold, 5, 9);
086: leaves[2] = doc.createLeafElement(paragraph, italic, 9, 15);
087: leaves[3] = doc.createLeafElement(paragraph, null, 15, 16);
088: ((BranchElement) paragraph).replace(0, 1, leaves);
089: BranchElement branch = (BranchElement) doc.createBranchElement(
090: root, null);
091: leaves = new Element[1];
092: leaves[0] = doc.createLeafElement(branch, null, 16, 21);
093: branch.replace(0, 0, leaves);
094: branch.addAttributes(boldFalse);
095: branch.addAttributes(italicFalse);
096: // Add this branch to the root
097: ((BranchElement) root).replace(1, 0, new Element[] { branch });
098: }
099:
100: @Override
101: protected void tearDown() throws Exception {
102: super .tearDown();
103: doc.writeUnlock();
104: }
105:
106: /**
107: * The remove region affects an element partially:
108: * from its start to its middle.
109: * No structure is changed.
110: */
111: public void testRemoveElementStart() throws Exception {
112: final Element boldElement = paragraph.getElement(1);
113: final int offset = boldElement.getStartOffset();
114: final int length = (boldElement.getEndOffset() - boldElement
115: .getStartOffset()) / 2;
116: buf.remove(offset, length, createEvent(offset, length));
117: assertEquals(0, getEdits(event).size());
118: assertChildren(paragraph,
119: new int[] { 0, 5, 5, 9, 9, 15, 15, 16 },
120: new AttributeSet[] { null, bold, italic, null });
121: }
122:
123: /**
124: * The remove region affects an element partially:
125: * from its middle to its end.
126: * No structure is changed.
127: */
128: public void testRemoveElementEnd() throws Exception {
129: final Element boldElement = paragraph.getElement(1);
130: final int offset = (boldElement.getStartOffset() + boldElement
131: .getEndOffset()) / 2;
132: final int length = boldElement.getEndOffset() - offset;
133: buf.remove(offset, length, createEvent(offset, length));
134: assertEquals(0, getEdits(event).size());
135: assertChildren(paragraph,
136: new int[] { 0, 5, 5, 9, 9, 15, 15, 16 },
137: new AttributeSet[] { null, bold, italic, null });
138: }
139:
140: /**
141: * An element fully falls into the remove region.
142: * This element is to be removed.
143: */
144: public void testRemoveElementFull() throws Exception {
145: final Element boldElement = paragraph.getElement(1);
146: final int offset = boldElement.getStartOffset();
147: final int length = boldElement.getEndOffset() - offset;
148: buf.remove(offset, length, createEvent(offset, length));
149: final List<?> edits = getEdits(event);
150: assertEquals(1, edits.size());
151: assertChange(edits.get(0), paragraph, 1, new int[] { 5, 9 },
152: new int[] {});
153: assertChildren(paragraph, new int[] { 0, 5, 9, 15, 15, 16 },
154: new AttributeSet[] { null, italic, null });
155: }
156:
157: /**
158: * The remove region affects two consecutive elements:
159: * from the start of the first to the middle of the second.
160: * The element fully contained in the remove region is to be removed
161: * (the first one).
162: */
163: public void testRemove2ElementsStart() throws Exception {
164: final Element plainElement = paragraph.getElement(0);
165: final Element boldElement = paragraph.getElement(1);
166: final int offset = plainElement.getStartOffset();
167: final int length = (boldElement.getStartOffset() + boldElement
168: .getEndOffset())
169: / 2 - offset;
170: buf.remove(offset, length, createEvent(offset, length));
171: final List<?> edits = getEdits(event);
172: assertEquals(1, edits.size());
173: assertChange(edits.get(0), paragraph, 0, new int[] { 0, 5 },
174: new int[] {});
175: assertChildren(paragraph, new int[] { 5, 9, 9, 15, 15, 16 },
176: new AttributeSet[] { bold, italic, null });
177: }
178:
179: /**
180: * The remove region affects two consecutive elements:
181: * from the middle of the first to the end of the second.
182: * The element fully contained in the remove region is to be removed
183: * (the second one).
184: */
185: public void testRemove2ElementsEnd() throws Exception {
186: final Element plainElement = paragraph.getElement(0);
187: final Element boldElement = paragraph.getElement(1);
188: final int offset = (plainElement.getStartOffset() + plainElement
189: .getEndOffset()) / 2;
190: final int length = boldElement.getEndOffset() - offset;
191: buf.remove(offset, length, createEvent(offset, length));
192: final List<?> edits = getEdits(event);
193: assertEquals(1, edits.size());
194: assertChange(edits.get(0), paragraph, 1, new int[] { 5, 9 },
195: new int[] {});
196: assertChildren(paragraph, new int[] { 0, 5, 9, 15, 15, 16 },
197: new AttributeSet[] { null, italic, null });
198: }
199:
200: /**
201: * The remove region affects two consecutive elements:
202: * from the middle of the first to the middle of the second.
203: * No structure is expected to change as the remove region contains
204: * no elements which fully fall into it.
205: */
206: public void testRemove2ElementsStartEnd() throws Exception {
207: final Element plainElement = paragraph.getElement(0);
208: final Element boldElement = paragraph.getElement(1);
209: final int offset = (plainElement.getStartOffset() + plainElement
210: .getEndOffset()) / 2;
211: final int length = (boldElement.getStartOffset() + boldElement
212: .getEndOffset())
213: / 2 - offset;
214: buf.remove(offset, length, createEvent(offset, length));
215: final List<?> edits = getEdits(event);
216: assertEquals(0, edits.size());
217: assertChildren(paragraph,
218: new int[] { 0, 5, 5, 9, 9, 15, 15, 16 },
219: new AttributeSet[] { null, bold, italic, null });
220: }
221:
222: /**
223: * The remove region contains two elements entirely.
224: * Both elements are to be removed.
225: */
226: public void testRemove2ElementsFull() throws Exception {
227: final Element plainElement = paragraph.getElement(0);
228: final Element boldElement = paragraph.getElement(1);
229: final int offset = plainElement.getStartOffset();
230: final int length = boldElement.getEndOffset() - offset;
231: buf.remove(offset, length, createEvent(offset, length));
232: final List<?> edits = getEdits(event);
233: assertEquals(1, edits.size());
234: assertChange(edits.get(0), paragraph, 0,
235: new int[] { 0, 5, 5, 9 }, new int[] {});
236: assertChildren(paragraph, new int[] { 9, 15, 15, 16 },
237: new AttributeSet[] { italic, null });
238: }
239:
240: /**
241: * This removes only one character from the document: the new line which
242: * separates two paragraphs.
243: * The paragraphs merge into one. All their child elements which are
244: * not entirely contained in the remove region are copied into the
245: * new paragraph.
246: */
247: public void testRemoveParagraphBreak() throws Exception {
248: final int offset = paragraph.getEndOffset() - 1;
249: final int length = 1;
250: ((AbstractElement) paragraph).addAttributes(bold);
251: buf.remove(offset, length, createEvent(offset, length));
252: final List<?> edits = getEdits(event);
253: assertEquals(1, edits.size());
254: assertChange(edits.get(0), root, 0,
255: new int[] { 0, 16, 16, 21 }, new int[] { 0, 21 });
256: final AbstractElement branch = (AbstractElement) root
257: .getElement(0);
258: assertChildren(branch, new int[] { 0, 5, 5, 9, 9, 15, 16, 21 },
259: new AttributeSet[] { null, bold, italic, null });
260: assertEquals(2, branch.getAttributeCount());
261: assertTrue(branch.isDefined(AttributeSet.ResolveAttribute));
262: assertTrue(branch.containsAttributes(bold));
263: }
264:
265: /**
266: * Removes an entire element before the paragraph break as well the break
267: * itself.
268: * Two paragraphs merge. The resulting paragraph doesn't contain the
269: * child elements which were entirely contained in the remove region.
270: */
271: public void testRemoveFullElementParagraphBreak() throws Exception {
272: final Element italicElement = paragraph.getElement(2);
273: final int offset = italicElement.getStartOffset();
274: final int length = paragraph.getEndOffset() - offset;
275: ((AbstractElement) paragraph).addAttributes(bold);
276: buf.remove(offset, length, createEvent(offset, length));
277: final List<?> edits = getEdits(event);
278: assertEquals(1, edits.size());
279: assertChange(edits.get(0), root, 0,
280: new int[] { 0, 16, 16, 21 }, new int[] { 0, 21 });
281: final AbstractElement branch = (AbstractElement) root
282: .getElement(0);
283: assertChildren(branch, new int[] { 0, 5, 5, 9, 16, 21 },
284: new AttributeSet[] { null, bold, null });
285: assertEquals(2, branch.getAttributeCount());
286: assertTrue(branch.isDefined(AttributeSet.ResolveAttribute));
287: assertTrue(branch.containsAttributes(bold));
288: }
289:
290: /**
291: * Remove an entire paragraph.
292: * Despite it is not necessary to change the document structure,
293: * it is modified as if only part of the paragraph was removed.
294: * I.e. a new branch is created, and it contains the children from
295: * the following paragraph only because all child elements of the first
296: * paragraph fall into the remove region entirely.
297: */
298: public void testRemoveFullParagraph() throws Exception {
299: final int offset = paragraph.getStartOffset();
300: final int length = paragraph.getEndOffset() - offset;
301: ((AbstractElement) paragraph).addAttributes(bold);
302: buf.remove(offset, length, createEvent(offset, length));
303: final List<?> edits = getEdits(event);
304: assertEquals(1, edits.size());
305: assertChange(edits.get(0), root, 0,
306: new int[] { 0, 16, 16, 21 }, new int[] { 16, 21 });
307: final AbstractElement branch = (AbstractElement) root
308: .getElement(0);
309: assertChildren(branch, new int[] { 16, 21 },
310: new AttributeSet[] { null });
311: assertEquals(2, branch.getAttributeCount());
312: assertTrue(branch.isDefined(AttributeSet.ResolveAttribute));
313: assertTrue(branch.containsAttributes(bold));
314: }
315:
316: private static void assertChange(final Object change,
317: final Element element, final int index,
318: final int[] removed, final int[] added) {
319: DefStyledDoc_Helpers.assertChange(change, element, index,
320: removed, added);
321: }
322:
323: private static void assertChildren(final Element element,
324: final int[] offsets, final AttributeSet[] attributes) {
325: DefStyledDoc_Helpers.assertChildren(element, offsets,
326: attributes);
327: }
328:
329: private DefaultDocumentEvent createEvent(final int offset,
330: final int length) {
331: event = doc.new DefaultDocumentEvent(offset, length,
332: EventType.CHANGE);
333: return event;
334: }
335:
336: private static List<?> getEdits(final DefaultDocumentEvent event) {
337: return DefStyledDoc_Helpers.getEdits(event);
338: }
339: }
|