PageRenderTime 78ms CodeModel.GetById 23ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre4/org/gjt/sp/jedit/buffer/PositionManager.java

#
Java | 704 lines | 530 code | 57 blank | 117 comment | 175 complexity | 77be3b54f91e39062f4ade6bd6c717e8 MD5 | raw file
  1/*
  2 * PositionManager.java - Manages positions
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2001, 2003 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit.buffer;
 24
 25//{{{ Imports
 26import javax.swing.text.*;
 27import org.gjt.sp.jedit.syntax.*;
 28import org.gjt.sp.jedit.Buffer;
 29import org.gjt.sp.jedit.Debug;
 30import org.gjt.sp.jedit.MiscUtilities;
 31import org.gjt.sp.util.IntegerArray;
 32import org.gjt.sp.util.Log;
 33//}}}
 34
 35/**
 36 * A class internal to jEdit's document model. You should not use it
 37 * directly. To improve performance, none of the methods in this class
 38 * check for out of bounds access, nor are they thread-safe. The
 39 * <code>Buffer</code> class, through which these methods must be
 40 * called through, implements such protection.
 41 *
 42 * @author Slava Pestov
 43 * @version $Id: PositionManager.java 4843 2003-08-04 03:19:13Z spestov $
 44 * @since jEdit 4.2pre3
 45 */
 46public class PositionManager
 47{
 48	//{{{ createPosition() method
 49	public synchronized Position createPosition(int offset)
 50	{
 51		//System.err.println(this + ": create " + offset);
 52		PosBottomHalf bh;
 53
 54		// moves gap to offset if necessary
 55		contentInserted(offset,0);
 56
 57		if(root == null)
 58			root = bh = new PosBottomHalf(offset - gapWidth);
 59		else
 60		{
 61			bh = root.find(offset);
 62
 63			if(bh == null)
 64			{
 65				bh = new PosBottomHalf(offset - gapWidth);
 66				bh.red = true;
 67				root.insert(bh);
 68				if(!Debug.DISABLE_POSITION_BALANCE)
 69					ibalance(bh);
 70			}
 71		}
 72
 73		/* if(root != null)
 74			check(root,new java.util.Vector()); */
 75		/* if(Debug.POSITION_DEBUG)
 76			root.dump(0); */
 77
 78		return new PosTopHalf(bh);
 79	} //}}}
 80
 81	//{{{ contentInserted() method
 82	public synchronized void contentInserted(int offset, int length)
 83	{
 84		if(root == null)
 85		{
 86			gapWidth = 0;
 87			return;
 88		}
 89
 90		if(gapWidth != 0)
 91		{
 92			if(gapOffset < offset)
 93				root.contentInserted(gapOffset,offset,gapWidth);
 94			else
 95				root.contentInserted(offset,gapOffset,-gapWidth);
 96		}
 97
 98		gapOffset = offset;
 99		gapWidth += length;
100	} //}}}
101
102	//{{{ contentRemoved() method
103	public synchronized void contentRemoved(int offset, int length)
104	{
105		if(root == null)
106		{
107			gapWidth = 0;
108			return;
109		}
110
111		if(gapWidth != 0 && gapOffset < offset)
112		{
113			root.contentRemoved(gapOffset,offset,offset + length,
114				gapWidth,true);
115		}
116		else if(gapWidth != 0 && gapOffset > offset + length)
117		{
118			root.contentRemoved(offset,offset + length,gapOffset,
119				gapWidth,false);
120		}
121		else
122		{
123			root.contentRemoved(offset,offset + length,
124				offset + length,gapWidth,false);
125		}
126
127		gapOffset = offset;
128		gapWidth -= length;
129
130	} //}}}
131
132	//{{{ Package-private members
133	/* so that PosBottomHalf can access without access$ methods */
134	int gapOffset;
135	int gapWidth;
136
137	//{{{ removePosition() method
138	private void removePosition(PosBottomHalf bh)
139	{
140		if(Debug.POSITION_DEBUG)
141			Log.log(Log.DEBUG,this,"killing " + bh);
142		PosBottomHalf r, x = bh.parent;
143
144		// if one of the siblings is null, make &this=non null sibling
145		if(bh.left == null)
146		{
147			r = bh.right;
148			if(bh.parent == null)
149			{
150				if(Debug.POSITION_DEBUG)
151					Log.log(Log.DEBUG,this,"simple/left: setting root to " + bh.right);
152				root = bh.right;
153			}
154			else
155			{
156				if(bh == bh.parent.left)
157					bh.parent.left = bh.right;
158				else
159					bh.parent.right = bh.right;
160			}
161			if(bh.right != null)
162			{
163				if(Debug.POSITION_DEBUG)
164					Log.log(Log.DEBUG,this,"134: " + bh.right+":"+bh.parent);
165				bh.right.parent = bh.parent;
166			}
167		}
168		else if(bh.right == null)
169		{
170			r = bh.left;
171			if(bh.parent == null)
172			{
173				if(Debug.POSITION_DEBUG)
174					Log.log(Log.DEBUG,this,"simple/right: setting root " + bh.left);
175				root = bh.left;
176			}
177			else
178			{
179				if(bh == bh.parent.left)
180					bh.parent.left = bh.left;
181				else
182					bh.parent.right = bh.left;
183			}
184			if(bh.left != null)
185			{
186				if(Debug.POSITION_DEBUG)
187					Log.log(Log.DEBUG,this,"155: "+bh.left+":" + bh.parent);
188				bh.left.parent = bh.parent;
189			}
190		}
191		// neither is null
192		else
193		{
194			PosBottomHalf nextInorder = bh.right;
195			r = nextInorder;
196			while(nextInorder.left != null)
197			{
198				nextInorder = nextInorder.left;
199				r = nextInorder.right;
200			}
201			// removing the root?
202			if(bh.parent == null)
203			{
204				if(Debug.POSITION_DEBUG)
205					Log.log(Log.DEBUG,this,"nextInorder: setting root to " + nextInorder);
206				root = nextInorder;
207			}
208			else
209			{
210				if(bh.parent.left == bh)
211					bh.parent.left = nextInorder;
212				else
213					bh.parent.right = nextInorder;
214			}
215
216			if(nextInorder != bh.right)
217			{
218				nextInorder.parent.left = nextInorder.right;
219				if(nextInorder.right != null)
220				{
221					if(Debug.POSITION_DEBUG)
222						Log.log(Log.DEBUG,this,"182: "+nextInorder.right+":" + nextInorder.parent);
223					nextInorder.right.parent = nextInorder.parent;
224				}
225				nextInorder.right = bh.right;
226				if(Debug.POSITION_DEBUG)
227					Log.log(Log.DEBUG,this,"186: "+nextInorder.right+":" + nextInorder);
228				nextInorder.right.parent = nextInorder;
229			}
230			x = nextInorder.parent;
231			if(Debug.POSITION_DEBUG)
232				Log.log(Log.DEBUG,this,"189: "+nextInorder+":" + bh.parent);
233			nextInorder.parent = bh.parent;
234			nextInorder.left = bh.left;
235			if(Debug.POSITION_DEBUG)
236				Log.log(Log.DEBUG,this,"192: "+nextInorder.left+":" + nextInorder);
237			nextInorder.left.parent = nextInorder;
238		}
239
240		if(!bh.red)
241		{
242			if(r != null && r.red)
243				r.red = false;
244			else if(x != null && r != null)
245			{
246				if(!Debug.DISABLE_POSITION_BALANCE)
247					rbalance(r,x);
248			}
249		}
250
251		if(Debug.POSITION_DEBUG && root != null)
252			root.dump(0);
253	} //}}}
254
255	//}}}
256
257	//{{{ Private members
258	private PosBottomHalf root;
259
260	//{{{ ibalance() method
261	private void ibalance(PosBottomHalf bh)
262	{
263		if(bh.parent.red)
264		{
265			PosBottomHalf u = bh.parent.parent;
266			PosBottomHalf w = bh.parent.sibling();
267			if(w != null && w.red)
268			{
269				if(Debug.POSITION_DEBUG)
270					Log.log(Log.DEBUG,this,"case 2");
271				bh.parent.red = false;
272				w.red = false;
273				if(u.parent == null)
274					u.red = false;
275				else
276				{
277					u.red = true;
278					ibalance(u);
279				}
280			}
281			else
282			{
283				if(Debug.POSITION_DEBUG)
284					Log.log(Log.DEBUG,this,"case 1");
285				PosBottomHalf b = bh.restructure();
286				b.red = false;
287				b.left.red = true;
288				b.right.red = true;
289			}
290		}
291	} //}}}
292
293	//{{{ rbalance() method
294	private void rbalance(PosBottomHalf r, PosBottomHalf x)
295	{
296		PosBottomHalf y = r.sibling();
297		PosBottomHalf z;
298
299		if(y.red)
300		{
301			if(Debug.POSITION_DEBUG)
302				Log.log(Log.DEBUG,this,"case 3");
303			if(x.right == y)
304				z = y.right;
305			else if(x.left == y)
306				z = y.left;
307			else
308				throw new InternalError();
309			PosBottomHalf b = z.restructure();
310			y.red = false;
311			x.red = true;
312
313			// different meaning of x and y
314			rbalance(r,r.sibling());
315		}
316		else
317		{
318			r.red = false;
319			if(y.left != null && y.left.red)
320				z = y.left;
321			else if(y.right != null && y.right.red)
322				z = y.right;
323			else
324			{
325				if(Debug.POSITION_DEBUG)
326					Log.log(Log.DEBUG,this,"case 2");
327				y.red = true;
328				if(x.red)
329					x.red = false;
330				else if(x.parent != null)
331					rbalance(x,x.parent);
332				return;
333			}
334
335			if(Debug.POSITION_DEBUG)
336				Log.log(Log.DEBUG,this,"case 1");
337
338			boolean oldXRed = x.red;
339			PosBottomHalf b = z.restructure();
340			b.left.red = false;
341			b.right.red = false;
342			b.red = oldXRed;
343		}
344	} //}}}
345
346	//}}}
347
348	//{{{ Inner classes
349
350	//{{{ PosTopHalf class
351	class PosTopHalf implements Position
352	{
353		PosBottomHalf bh;
354
355		//{{{ PosTopHalf constructor
356		PosTopHalf(PosBottomHalf bh)
357		{
358			this.bh = bh;
359			bh.ref();
360		} //}}}
361
362		//{{{ getOffset() method
363		public int getOffset()
364		{
365			return bh.getOffset();
366		} //}}}
367
368		//{{{ finalize() method
369		public void finalize()
370		{
371			synchronized(PositionManager.this)
372			{
373				bh.unref();
374			}
375		} //}}}
376	} //}}}
377
378	//{{{ PosBottomHalf class
379	class PosBottomHalf
380	{
381		int offset;
382		int ref;
383		PosBottomHalf parent;
384		PosBottomHalf left, right;
385		boolean red;
386
387		//{{{ PosBottomHalf constructor
388		PosBottomHalf(int offset)
389		{
390			this.offset = offset;
391		} //}}}
392
393		//{{{ getOffset() method
394		int getOffset()
395		{
396			if(offset >= gapOffset)
397				return offset + gapWidth;
398			else
399				return offset;
400		} //}}}
401
402		//{{{ dump() method
403		void dump(int level)
404		{
405			String ws = MiscUtilities.createWhiteSpace(level,0);
406			if(left != null)
407				left.dump(level+1);
408			else
409			{
410				Log.log(Log.DEBUG,this,ws + " /]");
411			}
412			Log.log(Log.DEBUG,this,ws + red + ":" + getOffset());
413			if(right != null)
414				right.dump(level+1);
415			else
416			{
417				Log.log(Log.DEBUG,this,ws + " \\]");
418			}
419		} //}}}
420
421		//{{{ insert() method
422		void insert(PosBottomHalf pos)
423		{
424			if(pos.getOffset() < getOffset())
425			{
426				if(left == null)
427				{
428					if(Debug.POSITION_DEBUG)
429						Log.log(Log.DEBUG,this,"382: "+pos+":" + this);
430					pos.parent = this;
431					left = pos;
432				}
433				else
434					left.insert(pos);
435			}
436			else
437			{
438				if(right == null)
439				{
440					if(Debug.POSITION_DEBUG)
441						Log.log(Log.DEBUG,this,"393: "+pos+":" + this);
442					pos.parent = this;
443					right = pos;
444				}
445				else
446					right.insert(pos);
447			}
448		} //}}}
449
450		//{{{ find() method
451		PosBottomHalf find(int offset)
452		{
453			if(getOffset() == offset)
454				return this;
455			else if(getOffset() < offset)
456			{
457				if(right == null)
458					return null;
459				else
460					return right.find(offset);
461			}
462			else
463			{
464				if(left == null)
465					return null;
466				else
467					return left.find(offset);
468			}
469		} //}}}
470
471		//{{{ sibling() method
472		PosBottomHalf sibling()
473		{
474			if(parent.left == this)
475				return parent.right;
476			else if(parent.right == this)
477				return parent.left;
478			else
479				throw new InternalError();
480		} //}}}
481
482		//{{{ contentInserted() method
483		/* update all nodes between start and end by length */
484		void contentInserted(int start, int end, int length)
485		{
486			if(getOffset() < start)
487			{
488				if(right != null)
489					right.contentInserted(start,end,length);
490			}
491			else if(getOffset() > end)
492			{
493				if(left != null)
494					left.contentInserted(start,end,length);
495			}
496			else
497			{
498				offset += length;
499				if(left != null)
500					left.contentInserted(start,end,length);
501				if(right != null)
502					right.contentInserted(start,end,length);
503			}
504		} //}}}
505
506		//{{{ contentRemoved() method
507		/* if bias: kill from p1 to p2, update from p2 to p3,
508		*  if !bias: update from p1 to p2, kill from p2 to p3 */
509		void contentRemoved(int p1, int p2, int p3, int length,
510			boolean bias)
511		{
512			if(getOffset() < p1)
513			{
514				if(right != null)
515					right.contentRemoved(p1,p2,p3,length,bias);
516			}
517			else if(getOffset() > p3)
518			{
519				if(left != null)
520					left.contentRemoved(p1,p2,p3,length,bias);
521			}
522			else
523			{
524				if(bias)
525				{
526					if(getOffset() >= p2)
527						offset = p2 - length;
528					else
529						offset += length;
530				}
531				else
532				{
533					if(getOffset() <= p2)
534						offset = p2 - length;
535					else
536						offset -= length;
537				}
538
539				if(left != null)
540					left.contentRemoved(p1,p2,p3,length,bias);
541				if(right != null)
542					right.contentRemoved(p1,p2,p3,length,bias);
543			}
544		} //}}}
545
546		//{{{ ref() method
547		void ref()
548		{
549			ref++;
550		} //}}}
551
552		//{{{ unref() method
553		void unref()
554		{
555			if(--ref == 0)
556				removePosition(this);
557		} //}}}
558
559		//{{{ restructure() method
560		private PosBottomHalf restructure()
561		{
562			// this method looks incomprehensible but really
563			// its quite simple. this node, its parent and its
564			// grandparent are rearranged such that in-ordering
565			// of the tree is preserved, and so that the tree
566			// looks like so:
567			//
568			//           (b)
569			//         /     \
570			//       (a)     (c)
571			//
572			// Where a/b/c are the first, second and third in an
573			// in-order traversal of this node, the parent and the
574			// grandparent.
575
576			// Temporary storage: { al, a, ar, b, cl, c, cr }
577			PosBottomHalf[] nodes;
578
579			// For clarity
580			PosBottomHalf u = parent.parent;
581
582			if(u.left == parent)
583			{
584				if(parent.left == this)
585				{
586					//     u
587					//    /
588					//   v
589					//  /
590					// z
591					if(Debug.POSITION_DEBUG)
592						Log.log(Log.DEBUG,this,"restructure 1");
593					// zl, z, zr, v, vr, u, ur
594					nodes = new PosBottomHalf[] {
595						left, this, right,
596						parent, parent.right,
597						u, u.right
598					};
599				}
600				else
601				{
602					//   u
603					//  /
604					// v
605					//  \
606					//   z
607					if(Debug.POSITION_DEBUG)
608						Log.log(Log.DEBUG,this,"restructure 2");
609					// vl, v, zl, z, zr, u, ur
610					nodes = new PosBottomHalf[] {
611						parent.left, parent, left,
612						this, right, u, u.right
613					};
614				}
615			}
616			else
617			{
618				if(parent.right == this)
619				{
620					// u
621					//  \
622					//   v
623					//    \
624					//     z
625					if(Debug.POSITION_DEBUG)
626						Log.log(Log.DEBUG,this,"restructure 3");
627					// ul, u, vl, v, zl, z, zr
628					nodes = new PosBottomHalf[] {
629						u.left, u, parent.left,
630						parent, left, this, right
631					};
632				}
633				else
634				{
635					// u
636					//  \
637					//   v
638					//  /
639					// z
640					if(Debug.POSITION_DEBUG)
641						Log.log(Log.DEBUG,this,"restructure 4");
642					// ul, u, zl, z, zr, v, vr
643					nodes = new PosBottomHalf[] {
644						u.left, u, left, this, right,
645						parent, parent.right
646					};
647				}
648			}
649
650			PosBottomHalf t = u.parent;
651			if(t != null)
652			{
653				if(t.left == u)
654					t.left = nodes[3];
655				else
656					t.right = nodes[3];
657			}
658			else
659			{
660				if(Debug.POSITION_DEBUG)
661					Log.log(Log.DEBUG,this,"restructure: setting root to " + nodes[3]);
662				root = nodes[3];
663			}
664
665			// Write-only code for constructing a meaningful tree
666			if(Debug.POSITION_DEBUG)
667				Log.log(Log.DEBUG,this,"583: "+nodes[1]+":" + nodes[3]);
668			if(nodes[0] != null)
669				nodes[0].parent = nodes[1];
670			nodes[1].parent = nodes[3];
671			nodes[1].left   = nodes[0];
672			nodes[1].right  = nodes[2];
673			if(nodes[2] != null)
674				nodes[2].parent = nodes[1];
675			if(Debug.POSITION_DEBUG)
676				Log.log(Log.DEBUG,this,"setting parent to " + t);
677			if(Debug.POSITION_DEBUG)
678				Log.log(Log.DEBUG,this,"590: " + nodes[3]+":" + t);
679			nodes[3].parent = t;
680			nodes[3].left   = nodes[1];
681			nodes[3].right  = nodes[5];
682
683			if(Debug.POSITION_DEBUG)
684				Log.log(Log.DEBUG,this,"595: "+nodes[5]+":" + nodes[3]);
685			if(nodes[4] != null)
686				nodes[4].parent = nodes[5];
687			nodes[5].parent = nodes[3];
688			nodes[5].left   = nodes[4];
689			nodes[5].right  = nodes[6];
690			if(nodes[6] != null)
691				nodes[6].parent = nodes[5];
692
693			return nodes[3];
694		} //}}}
695
696		//{{{ toString() method
697		public String toString()
698		{
699			return red + ":" + getOffset();
700		} //}}}
701	} //}}}
702
703	//}}}
704}