OSDN Git Service

Initial revision
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / text / AbstractDocument.java
1 /* AbstractDocument.java --
2    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing.text;
40
41 import java.io.PrintStream;
42 import java.io.Serializable;
43 import java.util.Dictionary;
44 import java.util.Enumeration;
45 import java.util.EventListener;
46 import java.util.Hashtable;
47 import java.util.Vector;
48
49 import javax.swing.event.DocumentEvent;
50 import javax.swing.event.DocumentListener;
51 import javax.swing.event.EventListenerList;
52 import javax.swing.event.UndoableEditEvent;
53 import javax.swing.event.UndoableEditListener;
54 import javax.swing.tree.TreeNode;
55 import javax.swing.undo.AbstractUndoableEdit;
56 import javax.swing.undo.CompoundEdit;
57 import javax.swing.undo.UndoableEdit;
58
59 public abstract class AbstractDocument
60   implements Document, Serializable
61 {
62   private static final long serialVersionUID = -116069779446114664L;
63   
64   protected static final String BAD_LOCATION = "document location failure";
65   
66   public static final String BidiElementName = "bidi level";
67   public static final String ContentElementName = "content";
68   public static final String ParagraphElementName = "paragraph";
69   public static final String SectionElementName = "section";
70   public static final String ElementNameAttribute = "$ename";
71
72   Content content;
73   AttributeContext context;
74   DocumentFilter documentFilter;
75
76   /** The documents properties. */
77   Dictionary properties;
78
79   protected EventListenerList listenerList = new EventListenerList();
80
81   protected AbstractDocument(Content doc)
82   {
83     this(doc, StyleContext.getDefaultStyleContext());
84   }
85
86   protected AbstractDocument(Content doc, AttributeContext ctx)
87   {
88     content = doc;
89     context = ctx;
90   }
91
92   // These still need to be implemented by a derived class:
93   public abstract Element getParagraphElement(int pos);
94
95   public abstract Element getDefaultRootElement();
96
97   protected Element createBranchElement(Element parent,
98                                         AttributeSet attributes)
99   {
100     return new BranchElement(parent, attributes);
101   }
102
103   protected Element createLeafElement(Element parent, AttributeSet attributes,
104                                       int start, int end)
105   {
106     return new LeafElement(parent, attributes, start, end);
107   }
108
109   public Position createPosition(final int offset) throws BadLocationException
110   {
111     if (offset < 0 || offset > getLength())
112       throw new BadLocationException(getText(0, getLength()), offset);
113
114     return new Position()
115       {
116         public int getOffset()
117         {
118           return offset;
119         }
120       };
121   }
122
123   protected void fireChangedUpdate(DocumentEvent event)
124   {
125     DocumentListener[] listeners = getDocumentListeners();
126
127     for (int index = 0; index < listeners.length; ++index)
128       listeners[index].changedUpdate(event);
129   }
130
131   protected void fireInsertUpdate(DocumentEvent event)
132   {
133     DocumentListener[] listeners = getDocumentListeners();
134
135     for (int index = 0; index < listeners.length; ++index)
136       listeners[index].insertUpdate(event);
137   }
138
139   protected void fireRemoveUpdate(DocumentEvent event)
140   {
141     DocumentListener[] listeners = getDocumentListeners();
142
143     for (int index = 0; index < listeners.length; ++index)
144       listeners[index].removeUpdate(event);
145   }
146
147   protected void fireUndoableEditUpdate(UndoableEditEvent event)
148   {
149     UndoableEditListener[] listeners = getUndoableEditListeners();
150
151     for (int index = 0; index < listeners.length; ++index)
152       listeners[index].undoableEditHappened(event);
153   }
154
155   public int getAsynchronousLoadPriority()
156   {
157     return 0;
158   }
159
160   protected AttributeContext getAttributeContext()
161   {
162     return context;
163   }
164
165   public Element getBidiRootElement()
166   {
167     return null;
168   }
169
170   protected Content getContent()
171   {
172     return content;
173   }
174
175   protected Thread getCurrentWriter()
176   {
177     return null;
178   }
179
180   public Dictionary getDocumentProperties()
181   {
182     // FIXME: make me thread-safe
183     if (properties == null)
184       properties = new Hashtable();
185
186     return properties;
187   }
188
189   public Position getEndPosition()
190   {
191     return new Position() 
192       {        
193         public int getOffset() 
194         { 
195           return getLength(); 
196         } 
197       };
198   }
199
200   public int getLength()
201   {
202     return content.length() - 1;
203   }
204
205   public EventListener[] getListeners(Class listenerType)
206   {
207     return listenerList.getListeners(listenerType);
208   }
209
210   public Object getProperty(Object key)
211   {
212     // FIXME: make me thread-safe
213     Object value = null;
214     if (properties != null)
215       value = properties.get(key);
216
217     return value;
218   }
219
220   public Element[] getRootElements()
221   {
222     Element[] elements = new Element[1];
223     elements[0] = getDefaultRootElement();
224     return elements;
225   }
226
227   public Position getStartPosition()
228   {
229     return new Position() 
230       {        
231         public int getOffset() 
232         { 
233           return 0; 
234         } 
235       };
236   }
237
238   public String getText(int offset, int length) throws BadLocationException
239   {
240     return content.getString(offset, length);
241   }
242
243   public void getText(int offset, int length, Segment segment)
244     throws BadLocationException
245   {
246     content.getChars(offset, length, segment);
247   }
248
249   public void insertString(int offset, String text, AttributeSet attributes)
250     throws BadLocationException
251   {
252     // Just return when no text to insert was given.
253     if (text == null || text.length() == 0)
254       return;
255     
256     DefaultDocumentEvent event =
257       new DefaultDocumentEvent(offset, text.length(),
258                                DocumentEvent.EventType.INSERT);
259     content.insertString(offset, text);
260     insertUpdate(event, attributes);
261     fireInsertUpdate(event);
262   }
263
264   protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr)
265   {
266   }
267
268   protected void postRemoveUpdate(DefaultDocumentEvent chng)
269   {
270   }
271
272   public void putProperty(Object key, Object value)
273   {
274     // FIXME: make me thread-safe
275     if (properties == null)
276       properties = new Hashtable();
277
278     properties.put(key, value);
279   }
280
281   public void readLock()
282   {
283   }
284
285   public void readUnlock()
286   {
287   }
288
289   public void remove(int offset, int length) throws BadLocationException
290   {
291     DefaultDocumentEvent event =
292       new DefaultDocumentEvent(offset, length,
293                                DocumentEvent.EventType.REMOVE);
294     removeUpdate(event);
295     content.remove(offset, length);
296     postRemoveUpdate(event);
297     fireRemoveUpdate(event);
298   }
299
300   /**
301    * Replaces some text in the document.
302    *
303    * @since 1.4
304    */
305   public void replace(int offset, int length, String text,
306                       AttributeSet attributes)
307     throws BadLocationException
308   {
309     remove(offset, length);
310     insertString(offset, text, attributes);
311   }
312
313   /**
314    * Adds a <code>DocumentListener</code> object to this document.
315    *
316    * @param listener the listener to add
317    */
318   public void addDocumentListener(DocumentListener listener)
319   {
320     listenerList.add(DocumentListener.class, listener);
321   }
322
323   /**
324    * Removes a <code>DocumentListener</code> object from this document.
325    *
326    * @param listener the listener to remove
327    */
328   public void removeDocumentListener(DocumentListener listener)
329   {
330     listenerList.remove(DocumentListener.class, listener);
331   }
332
333   /**
334    * Returns add added <code>DocumentListener</code> objects.
335    *
336    * @return an array of listeners
337    */
338   public DocumentListener[] getDocumentListeners()
339   {
340     return (DocumentListener[]) getListeners(DocumentListener.class);
341   }
342
343   /**
344    * Adds a <code>UndoableEditListener</code> object to this document.
345    *
346    * @param listener the listener to add
347    */
348   public void addUndoableEditListener(UndoableEditListener listener)
349   {
350     listenerList.add(UndoableEditListener.class, listener);
351   }
352
353   /**
354    * Removes a <code>UndoableEditListener</code> object from this document.
355    *
356    * @param listener the listener to remove
357    */
358   public void removeUndoableEditListener(UndoableEditListener listener)
359   {
360     listenerList.remove(UndoableEditListener.class, listener);
361   }
362
363   /**
364    * Returns add added <code>UndoableEditListener</code> objects.
365    *
366    * @return an array of listeners
367    */
368   public UndoableEditListener[] getUndoableEditListeners()
369   {
370     return (UndoableEditListener[]) getListeners(UndoableEditListener.class);
371   }
372
373   protected void removeUpdate(DefaultDocumentEvent chng)
374   {
375   }
376
377   public void render(Runnable r)
378   {
379   }
380
381   public void setAsynchronousLoadPriority(int p)
382   {
383   }
384
385   public void setDocumentProperties(Dictionary x)
386   {
387     // FIXME: make me thread-safe
388     properties = x;
389   }
390
391   protected void writeLock()
392   {
393   }
394
395   protected void writeUnlock()
396   {
397   }
398
399   /**
400    * @since 1.4
401    */
402   public DocumentFilter getDocumentFilter()
403   {
404     return documentFilter;
405   }
406
407   /**
408    * @since 1.4
409    */
410   public void setDocumentFilter(DocumentFilter filter)
411   {
412     this.documentFilter = filter;
413   }
414
415   public void dump(PrintStream out)
416   {
417     ((AbstractElement) getDefaultRootElement()).dump(out, 0);
418   }
419
420   public interface AttributeContext
421   {
422     AttributeSet addAttribute(AttributeSet old, Object name, Object value);
423
424     AttributeSet addAttributes(AttributeSet old, AttributeSet attributes);
425
426     AttributeSet getEmptySet();
427
428     void reclaim(AttributeSet attributes);
429
430     AttributeSet removeAttribute(AttributeSet old, Object name);
431
432     AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes);
433
434     AttributeSet removeAttributes(AttributeSet old, Enumeration names);
435   }
436
437   public interface Content
438   {
439     Position createPosition(int offset) throws BadLocationException;
440
441     int length();
442
443     UndoableEdit insertString(int where, String str)
444       throws BadLocationException;
445
446     UndoableEdit remove(int where, int nitems) throws BadLocationException;
447
448     String getString(int where, int len) throws BadLocationException;
449
450     void getChars(int where, int len, Segment txt) throws BadLocationException;
451   }
452
453   public abstract class AbstractElement
454     implements Element, MutableAttributeSet, TreeNode, Serializable
455   {
456     private static final long serialVersionUID = 1265312733007397733L;
457     int count;
458     int offset;
459
460     AttributeSet attributes;
461
462     Element element_parent;
463
464     TreeNode tree_parent;
465     Vector tree_children;
466
467     public AbstractElement(Element p, AttributeSet s)
468     {
469       element_parent = p;
470       attributes = s;
471     }
472
473     // TreeNode implementation
474
475     public abstract Enumeration children();
476       
477     public abstract boolean getAllowsChildren();
478       
479     public TreeNode getChildAt(int index)
480     {
481       return (TreeNode) tree_children.get(index);
482     }
483       
484     public int getChildCount()
485     {
486       return tree_children.size();
487     }
488       
489     public int getIndex(TreeNode node)
490     {
491       return tree_children.indexOf(node);
492     }
493
494     public TreeNode getParent()
495     {
496       return tree_parent;
497     }
498
499     public abstract boolean isLeaf();
500
501
502     // MutableAttributeSet support
503
504     public void addAttribute(Object name, Object value)
505     {
506       attributes = getAttributeContext().addAttribute(attributes, name, value);
507     }
508
509     public void addAttributes(AttributeSet attrs)
510     {
511       attributes = getAttributeContext().addAttributes(attributes, attrs);
512     }
513
514     public void removeAttribute(Object name)
515     {
516       attributes = getAttributeContext().removeAttribute(attributes, name);
517     }
518
519     public void removeAttributes(AttributeSet attrs)
520     {
521       attributes = getAttributeContext().removeAttributes(attributes, attrs);
522     }
523
524     public void removeAttributes(Enumeration names)
525     {
526       attributes = getAttributeContext().removeAttributes(attributes, names);
527     }
528
529     public void setResolveParent(AttributeSet parent)
530     {
531       attributes = getAttributeContext().addAttribute(attributes, ResolveAttribute, parent);
532     }
533
534
535     // AttributeSet interface support
536
537     public boolean containsAttribute(Object name, Object value)
538     {
539       return attributes.containsAttribute(name, value);
540     }
541
542     public boolean containsAttributes(AttributeSet attrs)
543     {
544       return attributes.containsAttributes(attrs);
545     }
546
547     public AttributeSet copyAttributes()
548     {
549       return attributes.copyAttributes();
550     }
551
552     public Object getAttribute(Object key)
553     {
554       return attributes.getAttribute(key);
555     }
556
557     public int getAttributeCount()
558     {
559       return attributes.getAttributeCount();
560     }
561       
562     public Enumeration getAttributeNames()
563     {
564       return attributes.getAttributeNames();
565     }
566       
567     public AttributeSet getResolveParent()
568     {
569       return attributes.getResolveParent();
570     }
571
572     public boolean isDefined(Object attrName)
573     {
574       return attributes.isDefined(attrName);
575     }
576       
577     public boolean isEqual(AttributeSet attrs) 
578     {
579       return attributes.isEqual(attrs);
580     }
581
582     // Element interface support
583
584     public AttributeSet getAttributes()
585     {
586       return attributes;
587     }
588
589     public Document getDocument()
590     {
591       return AbstractDocument.this;
592     }
593       
594     public abstract Element getElement(int index);
595       
596     public String getName()
597     {
598       return (String) getAttribute(NameAttribute);
599     }
600       
601     public Element getParentElement()
602     {
603       return element_parent;
604     }
605       
606     public abstract int getEndOffset();
607       
608     public abstract int getElementCount();
609       
610     public abstract int getElementIndex(int offset);
611       
612     public abstract int getStartOffset();
613
614     private void dumpElement(PrintStream stream, String indent, Element element)
615     {
616       System.out.println(indent + "<" + element.getName() +">");
617       
618       if (element.isLeaf())
619         {
620           int start = element.getStartOffset();
621           int end = element.getEndOffset();
622           String text = "";
623           try
624             {
625               text = getContent().getString(start, end - start);
626             }
627           catch (BadLocationException e)
628             {
629             }
630           System.out.println(indent + "  ["
631                              + start + ","
632                              + end + "]["
633                              + text + "]");
634         }
635       else
636         {
637           for (int i = 0; i < element.getElementCount(); ++i)
638             dumpElement(stream, indent + "  ", element.getElement(i));
639         }
640     }
641     
642     public void dump(PrintStream stream, int indent)
643     {
644       String indentStr = "";
645       for (int i = 0; i < indent; ++i)
646         indentStr += "  ";
647       dumpElement(stream, indentStr, this);
648     }
649   }
650
651   public class BranchElement extends AbstractElement
652   {
653     private static final long serialVersionUID = -8595176318868717313L;
654     
655     private Element[] children = new Element[0];
656
657     public BranchElement(Element parent, AttributeSet attributes)
658     {
659       super(parent, attributes);
660     }
661
662     public Enumeration children()
663     {
664       if (children.length == 0)
665         return null;
666
667       Vector tmp = new Vector();
668
669       for (int index = 0; index < children.length; ++index)
670         tmp.add(children[index]);
671       
672       return tmp.elements();
673     }
674
675     public boolean getAllowsChildren()
676     {
677       return true;
678     }
679
680     public Element getElement(int index)
681     {
682       if (index < 0 || index >= children.length)
683         return null;
684
685       return children[index];
686     }
687
688     public int getElementCount()
689     {
690       return children.length;
691     }
692
693     public int getElementIndex(int offset)
694     {
695       // XXX: There is surely a better algorithm
696       // as beginning from first element each time.
697       for (int index = 0; index < children.length; ++index)
698         {
699           Element elem = children[index];
700
701           if ((elem.getStartOffset() <= offset)
702               && (offset < elem.getEndOffset()))
703             return index;
704         }
705
706       return 0;
707     }
708
709     public int getEndOffset()
710     {
711       return children[children.length - 1].getEndOffset();
712     }
713
714     public String getName()
715     {
716       return ParagraphElementName;
717     }
718
719     public int getStartOffset()
720     {
721       return children[0].getStartOffset();
722     }
723
724     public boolean isLeaf()
725     {
726       return false;
727     }
728
729     public Element positionToElement(int position)
730     {
731       // XXX: There is surely a better algorithm
732       // as beginning from first element each time.
733       for (int index = 0; index < children.length; ++index)
734         {
735           Element elem = children[index];
736
737           if ((elem.getStartOffset() <= position)
738               && (position < elem.getEndOffset()))
739             return elem;
740         }
741
742       return null;
743     }
744
745     public void replace(int offset, int length, Element[] elements)
746     {
747       Element[] target = new Element[children.length - length
748                                      + elements.length];
749       System.arraycopy(children, 0, target, 0, offset);
750       System.arraycopy(elements, 0, target, offset, elements.length);
751       System.arraycopy(children, offset + length, target,
752                        offset + elements.length,
753                        children.length - offset - length);
754       children = target;
755     }
756
757     public String toString()
758     {
759       return ("BranchElement(" + getName() + ") "
760               + getStartOffset() + "," + getEndOffset() + "\n");
761     }
762   }
763
764   public class DefaultDocumentEvent extends CompoundEdit
765     implements DocumentEvent
766   {
767     private static final long serialVersionUID = -7406103236022413522L;
768     
769     private int offset;
770     private int length;
771     private DocumentEvent.EventType type;
772
773     public DefaultDocumentEvent(int offset, int length,
774                                 DocumentEvent.EventType type)
775     {
776       this.offset = offset;
777       this.length = length;
778       this.type = type;
779     }
780
781     public Document getDocument()
782     {
783       return AbstractDocument.this;
784     }
785
786     public int getLength()
787     {
788       return length;
789     }
790
791     public int getOffset()
792     {
793       return offset;
794     }
795
796     public DocumentEvent.EventType getType()
797     {
798       return type;
799     }
800
801     public DocumentEvent.ElementChange getChange(Element elem)
802     {
803       return null;
804     }
805   }
806
807   public static class ElementEdit extends AbstractUndoableEdit
808     implements DocumentEvent.ElementChange
809   {
810     private static final long serialVersionUID = -1216620962142928304L;
811
812     private Element elem;
813     private int index;
814     private Element[] removed;
815     private Element[] added;
816     
817     public ElementEdit(Element elem, int index,
818                        Element[] removed, Element[] added)
819     {
820       this.elem = elem;
821       this.index = index;
822       this.removed = removed;
823       this.added = added;
824     }
825
826     public Element[] getChildrenAdded()
827     {
828       return added;
829     }
830     
831     public Element[] getChildrenRemoved()
832     {
833       return removed;
834     }
835
836     public Element getElement()
837     {
838       return elem;
839     }
840
841     public int getIndex()
842     {
843       return index;
844     }
845   }
846
847   public class LeafElement extends AbstractElement
848   {
849     private static final long serialVersionUID = 5115368706941283802L;
850     int start;
851     int end;
852
853     public LeafElement(Element parent, AttributeSet attributes, int start,
854                        int end)
855     {
856       super(parent, attributes);
857       this.start = start;
858       this.end = end;
859     }
860
861     public Enumeration children()
862     {
863       return null;
864     }
865
866     public boolean getAllowsChildren()
867     {
868       return false;
869     }
870
871     public Element getElement(int index)
872     {
873       return null;
874     }
875
876     public int getElementCount()
877     {
878       return 0;
879     }
880
881     public int getElementIndex(int offset)
882     {
883       return -1;
884     }
885
886     public int getEndOffset()
887     {
888       return end;
889     }
890
891     public String getName()
892     {
893       return ContentElementName;
894     }
895
896     public int getStartOffset()
897     {
898       return start;
899     }
900
901     public boolean isLeaf()
902     {
903       return true;
904     }
905
906     public String toString()
907     {
908       return ("LeafElement(" + getName() + ") "
909               + getStartOffset() + "," + getEndOffset() + "\n");
910     }
911   }
912 }