/* * PositionManager.java - Manages positions * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2001, 2005 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.buffer; //{{{ Imports import javax.swing.text.Position; import java.util.*; import org.gjt.sp.util.Log; //}}} /** * A class internal to jEdit's document model. You should not use it * directly. * <p>Positions are created explicitly and removed implicitly, when * there are no more references to it. For this implicit removal to work * a top (referenced outside) and a bottom half (referenced internally) * of the position are implemented separately. * * @author Slava Pestov * @version $Id: PositionManager.java 21831 2012-06-18 22:54:17Z ezust $ * @since jEdit 4.2pre3 */ class PositionManager { //{{{ PositionManager constructor public PositionManager(JEditBuffer buffer) { this.buffer = buffer; } //}}} //{{{ createPosition() method /** No explicit removal is required. Unreferencing is enough. */ public synchronized Position createPosition(int offset) { PosBottomHalf bh = new PosBottomHalf(offset); PosBottomHalf existing = positions.get(bh); if(existing == null) { positions.put(bh,bh); existing = bh; } return new PosTopHalf(existing); } //}}} //{{{ contentInserted() method public synchronized void contentInserted(int offset, int length) { if(positions.isEmpty()) return; /* get all positions from offset to the end, inclusive */ Iterator<PosBottomHalf> iter = positions.tailMap(new PosBottomHalf(offset)) .keySet().iterator(); iteration = true; while(iter.hasNext()) { iter.next().contentInserted(offset,length); } iteration = false; } //}}} //{{{ contentRemoved() method public synchronized void contentRemoved(int offset, int length) { if(positions.isEmpty()) return; /* get all positions from offset to the end, inclusive */ Iterator<PosBottomHalf> iter = positions.tailMap(new PosBottomHalf(offset)) .keySet().iterator(); iteration = true; while(iter.hasNext()) { iter.next().contentRemoved(offset,length); } iteration = false; } //}}} boolean iteration; //{{{ Private members private JEditBuffer buffer; private SortedMap<PosBottomHalf, PosBottomHalf> positions = new TreeMap<PosBottomHalf, PosBottomHalf>(); //}}} //{{{ Inner classes //{{{ PosTopHalf class /** A wrapper for real position handling done by * <code>PosBottomHalf</code>, so Top means the part that is * visible. When there are no more references * to <code>PosTopHalf</code> and garbage * collector eats it, the position is removed together with its * bottom half. */ class PosTopHalf implements Position { final PosBottomHalf bh; //{{{ PosTopHalf constructor PosTopHalf(PosBottomHalf bh) { this.bh = bh; bh.ref(); } //}}} //{{{ getOffset() method public int getOffset() { return bh.offset; } //}}} //{{{ finalize() method @Override protected void finalize() { synchronized(PositionManager.this) { bh.unref(); } } //}}} } //}}} //{{{ PosBottomHalf class /** 'bottom' means the part * that is not visible outside and stays only here in * <code>positions</code> map.*/ class PosBottomHalf implements Comparable<PosBottomHalf> { int offset; int ref; //{{{ PosBottomHalf constructor PosBottomHalf(int offset) { this.offset = offset; } //}}} //{{{ ref() method void ref() { ref++; } //}}} //{{{ unref() method void unref() { if(--ref == 0) positions.remove(this); } //}}} //{{{ contentInserted() method void contentInserted(int offset, int length) { if(offset > this.offset) throw new ArrayIndexOutOfBoundsException(); this.offset += length; checkInvariants(); } //}}} //{{{ contentRemoved() method void contentRemoved(int offset, int length) { if(offset > this.offset) throw new ArrayIndexOutOfBoundsException(); if(this.offset <= offset + length) this.offset = offset; else this.offset -= length; checkInvariants(); } //}}} //{{{ equals() method @Override public boolean equals(Object o) { if(!(o instanceof PosBottomHalf)) return false; return ((PosBottomHalf)o).offset == offset; } //}}} //{{{ compareTo() method public int compareTo(PosBottomHalf posBottomHalf) { if(iteration) Log.log(Log.ERROR,this,"Consistency failure"); return offset - posBottomHalf.offset; } //}}} //{{{ checkInvariants() method private void checkInvariants() { if(offset < 0 || offset > buffer.getLength()) throw new ArrayIndexOutOfBoundsException(); } //}}} } //}}} //}}} }