PageRenderTime 101ms CodeModel.GetById 85ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

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

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