001: /*
002: * TagFileManager.java
003: *
004: * Copyright (C) 1998-2003 Peter Graves
005: * $Id: TagFileManager.java,v 1.3 2003/06/29 00:19:34 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import java.io.BufferedReader;
025: import java.io.BufferedWriter;
026: import java.io.IOException;
027: import java.io.InputStreamReader;
028: import java.io.OutputStreamWriter;
029: import java.util.ArrayList;
030: import java.util.Collections;
031: import java.util.Comparator;
032: import java.util.Iterator;
033: import java.util.List;
034: import java.util.Vector;
035:
036: public final class TagFileManager extends Thread {
037: // Version of tag file format.
038: private static final String VERSION = "1";
039:
040: private final File tagFileDir;
041: private final TagFileCatalog catalog;
042:
043: private Vector queue = new Vector();
044: private boolean enabled = true;
045:
046: private TagFileCache cache;
047:
048: public TagFileManager() {
049: super ("tag file manager");
050: setPriority(Thread.MIN_PRIORITY);
051: setDaemon(true);
052: tagFileDir = File.getInstance(Directories.getEditorDirectory(),
053: "tagfiles");
054: catalog = new TagFileCatalog(tagFileDir);
055: if (initialize())
056: start();
057: }
058:
059: public void run() {
060: while (true) {
061: QueueEntry entry = getEntryFromQueue();
062: refreshTagFile(entry);
063: }
064: }
065:
066: private synchronized boolean initialize() {
067: if (!tagFileDir.isDirectory()) {
068: tagFileDir.mkdirs();
069: if (!tagFileDir.isDirectory()) {
070: Log.error("TagFileManager.run can't make directory "
071: .concat(tagFileDir.canonicalPath()));
072: queue = null;
073: return false;
074: }
075: }
076: catalog.load();
077: cleanup();
078: return true;
079: }
080:
081: public synchronized void addToQueue(File dir, Mode mode) {
082: if (queue == null)
083: return;
084: if (dir.isRemote())
085: return;
086: QueueEntry entry = new QueueEntry(dir, mode);
087: for (int i = queue.size() - 1; i >= 0; i--) {
088: if (entry.equals(queue.get(i)))
089: return;
090: }
091: queue.add(entry);
092: notify();
093: }
094:
095: public synchronized void setEnabled(boolean b) {
096: boolean wasEnabled = enabled;
097: enabled = b;
098: if (enabled && !wasEnabled)
099: notify();
100: }
101:
102: private boolean ready() {
103: return enabled && queue.size() > 0;
104: }
105:
106: private synchronized QueueEntry getEntryFromQueue() {
107: while (!ready()) {
108: try {
109: wait();
110: } catch (InterruptedException e) {
111: Log.error(e);
112: }
113: }
114: return (QueueEntry) queue.remove(0);
115: }
116:
117: private final File getTagFile(File dir, Mode mode) {
118: return catalog.getTagFile(dir, mode);
119: }
120:
121: public void makeTagFile(File dir, Mode mode) {
122: Debug.assertTrue(mode != null);
123: try {
124: if (dir.isRemote())
125: return;
126: File oldTagfile = getTagFile(dir, mode);
127: File tagfile = Utilities.getTempFile(tagFileDir);
128: if (tagfile != null) {
129: String[] files = dir.list();
130: if (files != null) {
131: BufferedWriter writer = new BufferedWriter(
132: new OutputStreamWriter(tagfile
133: .getOutputStream()));
134: writer.write(VERSION);
135: writer.write('\n');
136: for (int i = 0; i < files.length; i++) {
137: File file = File.getInstance(dir, files[i]);
138: if (mode.accepts(file.getName())
139: && file.isFile()) {
140: SystemBuffer buf = new SystemBuffer(file);
141: buf.load();
142: Tagger tagger = mode.getTagger(buf);
143: tagger.run();
144: tagger.writeTags(writer);
145: buf._empty();
146: }
147: }
148: writer.flush();
149: writer.close();
150: if (tagfile.length() == 0) {
151: tagfile.delete();
152: } else {
153: catalog.addEntry(dir, tagfile, mode);
154: catalog.save();
155: if (oldTagfile != null) {
156: oldTagfile.delete();
157: if (cache != null)
158: cache.remove(oldTagfile);
159: }
160: }
161: }
162: }
163: } catch (Exception e) {
164: Log.error(e);
165: }
166: }
167:
168: private boolean isTagFileOutOfDate(QueueEntry entry) {
169: if (entry.directory.isRemote())
170: return false;
171: File tagfile = getTagFile(entry.directory, entry.mode);
172: if (tagfile == null)
173: return true;
174: if (!tagfile.exists())
175: return true;
176: long tagfileLastModified = tagfile.lastModified();
177: String[] files = entry.directory.list();
178: if (files != null) {
179: for (int i = 0; i < files.length; i++) {
180: File file = File.getInstance(entry.directory, files[i]);
181: if (!file.isFile())
182: continue;
183: if (entry.mode.accepts(file.getName()))
184: if (file.lastModified() > tagfileLastModified)
185: return true;
186: }
187: }
188: return false;
189: }
190:
191: private void refreshTagFile(QueueEntry queueEntry) {
192: if (isTagFileOutOfDate(queueEntry))
193: makeTagFile(queueEntry.directory, queueEntry.mode);
194: }
195:
196: private synchronized void cleanup() {
197: final int days = 5;
198: final long cutoff = System.currentTimeMillis() - 24 * 60 * 60
199: * 1000 * days;
200: String[] files = tagFileDir.list();
201: for (int i = 0; i < files.length; i++) {
202: String name = files[i];
203: if (name.equals("catalog")) {
204: // It's the catalog file.
205: continue;
206: }
207: File file = File.getInstance(tagFileDir, name);
208: if (!file.isFile())
209: continue;
210: if (catalog.containsTagFileName(name)
211: && file.lastModified() > cutoff)
212: continue;
213: file.delete();
214: }
215: catalog.update();
216: }
217:
218: public List getTags(File directory, Mode mode) {
219: File tagFile = getTagFile(directory, mode);
220: if (tagFile == null) {
221: Log.debug("getTags no tag file " + directory + " " + mode);
222: return null;
223: }
224: if (!tagFile.isFile()) {
225: Log.debug("getTags tag file doesn't exist");
226: return null;
227: }
228: List tags = null;
229: // First checked cached tag files.
230: if (cache != null)
231: tags = cache.getTags(tagFile);
232: if (tags == null) {
233: try {
234: BufferedReader reader = new BufferedReader(
235: new InputStreamReader(tagFile.getInputStream()));
236: String s = reader.readLine();
237: if (s != null && s.equals(VERSION)) {
238: tags = new ArrayList();
239: while ((s = reader.readLine()) != null) {
240: GlobalTag tag = GlobalTag.makeGlobalTag(s);
241: if (tag != null)
242: tags.add(tag);
243: }
244: } else {
245: Log.warn("getTags wrong version " + directory + " "
246: + mode);
247: }
248: reader.close();
249: } catch (IOException e) {
250: Log.error(e);
251: }
252: if (tags != null) {
253: if (cache == null)
254: cache = new TagFileCache();
255: cache.add(directory, mode.toString(), tagFile, tags);
256: } else
257: tagFile.delete();
258: }
259: return tags;
260: }
261:
262: private static class QueueEntry {
263: final File directory;
264: final Mode mode;
265:
266: QueueEntry(File directory, Mode mode) {
267: this .directory = directory;
268: this .mode = mode;
269: }
270:
271: public boolean equals(Object obj) {
272: if (this == obj)
273: return true;
274: if (obj instanceof QueueEntry) {
275: QueueEntry qe = (QueueEntry) obj;
276: if (!directory.equals(qe.directory))
277: return false;
278: // Same directory.
279: if (mode == qe.mode)
280: return true;
281: if (mode != null && mode.equals(qe.mode))
282: return true;
283: }
284: return false;
285: }
286: }
287:
288: private static class TagFileCache {
289: private static final int MAX_FILES = 5;
290:
291: private ArrayList list = new ArrayList(MAX_FILES);
292:
293: TagFileCache() {
294: }
295:
296: synchronized List getTags(File tagFile) {
297: Iterator iter = list.iterator();
298: while (iter.hasNext()) {
299: CacheEntry entry = (CacheEntry) iter.next();
300: if (entry.tagFile.equals(tagFile)) {
301: entry.lastAccess = System.currentTimeMillis();
302: // Move entry to top of list.
303: ArrayList newList = new ArrayList(MAX_FILES);
304: newList.add(entry);
305: for (int i = 0; i < list.size(); i++) {
306: CacheEntry e = (CacheEntry) list.get(i);
307: if (e != entry)
308: newList.add(e);
309: }
310: Debug.assertTrue(newList.size() == list.size());
311: list = newList;
312: checkOrder();
313: return entry.tags;
314: }
315: }
316: return null;
317: }
318:
319: synchronized void add(File directory, String modeName,
320: File tagFile, List tags) {
321: CacheEntry entry = new CacheEntry(directory, modeName,
322: tagFile, tags);
323: ArrayList newList = new ArrayList(MAX_FILES);
324: newList.add(entry);
325: int count = 1;
326: for (int i = 0; i < list.size() && count < MAX_FILES; i++) {
327: CacheEntry e = (CacheEntry) list.get(i);
328: if (!e.tagFile.equals(tagFile)) {
329: newList.add(e);
330: ++count;
331: }
332: }
333: list = newList;
334: checkOrder();
335: }
336:
337: synchronized void remove(File tagFile) {
338: Iterator iter = list.iterator();
339: while (iter.hasNext()) {
340: CacheEntry entry = (CacheEntry) iter.next();
341: if (entry.tagFile.equals(tagFile)) {
342: iter.remove();
343: checkOrder();
344: Log.debug("cache remove size = " + list.size());
345: return;
346: }
347: }
348: // Not found.
349: }
350:
351: // Only called from synchronized methods.
352: void checkOrder() {
353: if (Editor.isDebugEnabled()) {
354: for (int i = 0; i < list.size() - 1; i++) {
355: CacheEntry entry1 = (CacheEntry) list.get(i);
356: CacheEntry entry2 = (CacheEntry) list.get(i + 1);
357: if (entry1.lastAccess < entry2.lastAccess)
358: Debug.bug();
359: }
360: // dump();
361: }
362: }
363:
364: // Only called from synchronized methods.
365: // void dump()
366: // {
367: // for (int i = 0; i < list.size(); i++)
368: // Log.debug(String.valueOf(i) + " " + list.get(i).toString());
369: // }
370: }
371:
372: private static class CacheEntry {
373: final File directory; // Needed for debugging only!
374: final String modeName; // Needed for debugging only!
375: final File tagFile;
376: List tags;
377: long lastAccess; // Needed for debugging only!
378:
379: CacheEntry(File directory, String modeName, File tagFile,
380: List tags) {
381: this .directory = directory;
382: this .modeName = modeName;
383: this .tagFile = tagFile;
384: this .tags = tags;
385: this .lastAccess = System.currentTimeMillis();
386: }
387:
388: public String toString() {
389: return directory.canonicalPath() + " " + modeName + " "
390: + String.valueOf(lastAccess);
391: }
392: }
393: }
|