PageRenderTime 88ms CodeModel.GetById 54ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/buffer/KillRing.java

#
Java | 407 lines | 307 code | 52 blank | 48 comment | 52 complexity | 68b0924105726f95689c08b71de8cb13 MD5 | raw file
  1/*
  2 * KillRing.java - Stores deleted text
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 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
 25import com.microstar.xml.*;
 26import javax.swing.event.ListDataListener;
 27import javax.swing.ListModel;
 28import java.io.*;
 29import java.util.*;
 30import org.gjt.sp.jedit.*;
 31import org.gjt.sp.util.Log;
 32
 33public class KillRing
 34{
 35	//{{{ propertiesChanged() method
 36	public static void propertiesChanged()
 37	{
 38		int newSize = jEdit.getIntegerProperty("history",25);
 39		if(ring == null)
 40			ring = new UndoManager.Remove[newSize];
 41		else if(newSize != ring.length)
 42		{
 43			UndoManager.Remove[] newRing = new UndoManager.Remove[
 44				newSize];
 45			ListModel model = new RingListModel();
 46			int newCount = Math.min(model.getSize(),newSize);
 47			for(int i = 0; i < newCount; i++)
 48			{
 49				newRing[i] = (UndoManager.Remove)
 50					model.getElementAt(i);
 51			}
 52			ring = newRing;
 53			count = newCount;
 54			wrap = false;
 55		}
 56		else if(count == ring.length)
 57		{
 58			count = 0;
 59			wrap = true;
 60		}
 61	} //}}}
 62
 63	//{{{ getListModel() method
 64	public static ListModel getListModel()
 65	{
 66		return new RingListModel();
 67	} //}}}
 68
 69	//{{{ load() method
 70	public static void load()
 71	{
 72		String settingsDirectory = jEdit.getSettingsDirectory();
 73		if(settingsDirectory == null)
 74			return;
 75
 76		File killRing = new File(MiscUtilities.constructPath(
 77			settingsDirectory,"killring.xml"));
 78		if(!killRing.exists())
 79			return;
 80
 81		killRingModTime = killRing.lastModified();
 82		Log.log(Log.MESSAGE,KillRing.class,"Loading killring.xml");
 83
 84		KillRingHandler handler = new KillRingHandler();
 85		XmlParser parser = new XmlParser();
 86		Reader in = null;
 87		parser.setHandler(handler);
 88		try
 89		{
 90			in = new BufferedReader(new FileReader(killRing));
 91			parser.parse(null, null, in);
 92		}
 93		catch(XmlException xe)
 94		{
 95			int line = xe.getLine();
 96			String message = xe.getMessage();
 97			Log.log(Log.ERROR,KillRing.class,killRing + ":" + line
 98				+ ": " + message);
 99		}
100		catch(FileNotFoundException fnf)
101		{
102			//Log.log(Log.DEBUG,BufferHistory.class,fnf);
103		}
104		catch(Exception e)
105		{
106			Log.log(Log.ERROR,KillRing.class,e);
107		}
108		finally
109		{
110			try
111			{
112				if(in != null)
113					in.close();
114			}
115			catch(IOException io)
116			{
117				Log.log(Log.ERROR,KillRing.class,io);
118			}
119		}
120
121		ring = (UndoManager.Remove[])handler.list.toArray(
122			new UndoManager.Remove[handler.list.size()]);
123		count = ring.length;
124	} //}}}
125
126	//{{{ save() method
127	public static void save()
128	{
129		String settingsDirectory = jEdit.getSettingsDirectory();
130		if(settingsDirectory == null)
131			return;
132
133		File file1 = new File(MiscUtilities.constructPath(
134			settingsDirectory, "#killring.xml#save#"));
135		File file2 = new File(MiscUtilities.constructPath(
136			settingsDirectory, "killring.xml"));
137		if(file2.exists() && file2.lastModified() != killRingModTime)
138		{
139			Log.log(Log.WARNING,KillRing.class,file2
140				+ " changed on disk; will not save recent"
141				+ " files");
142			return;
143		}
144
145		jEdit.backupSettingsFile(file2);
146
147		Log.log(Log.MESSAGE,KillRing.class,"Saving killring.xml");
148
149		String lineSep = System.getProperty("line.separator");
150
151		BufferedWriter out = null;
152
153		try
154		{
155			out = new BufferedWriter(new FileWriter(file1));
156
157			out.write("<?xml version=\"1.0\"?>");
158			out.write(lineSep);
159			out.write("<!DOCTYPE KILLRING SYSTEM \"killring.dtd\">");
160			out.write(lineSep);
161			out.write("<KILLRING>");
162			out.write(lineSep);
163
164			ListModel model = getListModel();
165			int size = model.getSize();
166			for(int i = size - 1; i >=0; i--)
167			{
168				out.write("<ENTRY>");
169				out.write(MiscUtilities.charsToEntities(
170					model.getElementAt(i).toString()));
171				out.write("</ENTRY>");
172				out.write(lineSep);
173			}
174
175			out.write("</KILLRING>");
176			out.write(lineSep);
177
178			out.close();
179
180			/* to avoid data loss, only do this if the above
181			 * completed successfully */
182			file2.delete();
183			file1.renameTo(file2);
184		}
185		catch(Exception e)
186		{
187			Log.log(Log.ERROR,KillRing.class,e);
188		}
189		finally
190		{
191			try
192			{
193				if(out != null)
194					out.close();
195			}
196			catch(IOException e)
197			{
198			}
199		}
200
201		killRingModTime = file2.lastModified();
202	} //}}}
203
204	//{{{ Package-private members
205	static UndoManager.Remove[] ring;
206	static int count;
207	static boolean wrap;
208
209	//{{{ changed() method
210	static void changed(UndoManager.Remove rem)
211	{
212		if(rem.inKillRing)
213		{
214			// compare existing entries' hashcode with this
215			int length = (wrap ? ring.length : count);
216			int kill = -1;
217			boolean duplicate = false;
218
219			for(int i = 0; i < length; i++)
220			{
221				if(ring[i] != rem
222					&& ring[i].hashcode == rem.hashcode
223					&& ring[i].str.equals(rem.str))
224				{
225					// we don't want duplicate
226					// entries in the kill ring
227					kill = i;
228					break;
229				}
230			}
231
232			if(kill != -1)
233				remove(kill);
234		}
235		else
236			add(rem);
237	} //}}}
238
239	//{{{ add() method
240	static void add(UndoManager.Remove rem)
241	{
242		// compare existing entries' hashcode with this
243		int length = (wrap ? ring.length : count);
244		for(int i = 0; i < length; i++)
245		{
246			if(ring[i].hashcode == rem.hashcode)
247			{
248				// strings might be equal!
249				if(ring[i].str.equals(rem.str))
250				{
251					// we don't want duplicate entries
252					// in the kill ring
253					return;
254				}
255			}
256		}
257
258		// no duplicates, check for all-whitespace string
259		boolean allWhitespace = true;
260		for(int i = 0; i < rem.str.length(); i++)
261		{
262			if(!Character.isWhitespace(rem.str.charAt(i)))
263			{
264				allWhitespace = false;
265				break;
266			}
267		}
268
269		if(allWhitespace)
270			return;
271
272		rem.inKillRing = true;
273
274		if(ring[count] != null)
275			ring[count].inKillRing = false;
276
277		ring[count] = rem;
278		if(++count >= ring.length)
279		{
280			wrap = true;
281			count = 0;
282		}
283	} //}}}
284
285	//{{{ remove() method
286	static void remove(int i)
287	{
288		if(wrap)
289		{
290			UndoManager.Remove[] newRing = new UndoManager.Remove[
291				ring.length];
292			int newCount = 0;
293			for(int j = 0; j < ring.length; j++)
294			{
295				int index;
296				if(j < count)
297					index = count - j - 1;
298				else
299					index = count + ring.length - j - 1;
300
301				if(i == index)
302				{
303					ring[index].inKillRing = false;
304					continue;
305				}
306
307				newRing[newCount++] = ring[index];
308			}
309			ring = newRing;
310			count = newCount;
311			wrap = false;
312		}
313		else
314		{
315			System.arraycopy(ring,i + 1,ring,i,count - i - 1);
316			count--;
317		}
318	} //}}}
319
320	//}}}
321
322	//{{{ Private members
323	private static long killRingModTime;
324
325	private KillRing() {}
326	//}}}
327
328	//{{{ RingListModel class
329	static class RingListModel implements ListModel
330	{
331		public void addListDataListener(ListDataListener listener)
332		{
333		}
334
335		public void removeListDataListener(ListDataListener listener)
336		{
337		}
338
339		public Object getElementAt(int index)
340		{
341			UndoManager.Remove rem;
342
343			if(wrap)
344			{
345				if(index < count)
346					rem = ring[count - index - 1];
347				else
348					rem = ring[count + ring.length - index - 1];
349			}
350			else
351				rem = ring[count - index - 1];
352
353			return rem;
354		}
355
356		public int getSize()
357		{
358			if(wrap)
359				return ring.length;
360			else
361				return count;
362		}
363	} //}}}
364
365	//{{{ KillRingHandler class
366	static class KillRingHandler extends HandlerBase
367	{
368		List list = new LinkedList();
369
370		public Object resolveEntity(String publicId, String systemId)
371		{
372			if("killring.dtd".equals(systemId))
373			{
374				// this will result in a slight speed up, since we
375				// don't need to read the DTD anyway, as AElfred is
376				// non-validating
377				return new StringReader("<!-- -->");
378			}
379
380			return null;
381		}
382
383		public void doctypeDecl(String name, String publicId,
384			String systemId) throws Exception
385		{
386			if("KILLRING".equals(name))
387				return;
388
389			Log.log(Log.ERROR,this,"killring.xml: DOCTYPE must be KILLRING");
390		}
391
392		public void endElement(String name)
393		{
394			if(name.equals("ENTRY"))
395			{
396				list.add(new UndoManager.Remove(null,0,0,charData));
397			}
398		}
399
400		public void charData(char[] ch, int start, int length)
401		{
402			charData = new String(ch,start,length);
403		}
404
405		private String charData;
406	} //}}}
407}