further fixes to the change reporting framework
[mir.git] / source / mir / changetracker / ChangeTracker.java
1 /*
2  * Copyright (C) 2001-2006 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with  any library licensed under the Apache Software License,
22  * and distribute linked combinations including the two.  You must obey the
23  * GNU General Public License in all respects for all of the code used other than
24  * the above mentioned libraries.  If you modify this file, you may extend this
25  * exception to your version of the file, but you are not obligated to do so.
26  * If you do not wish to do so, delete this exception statement from your version.
27  */
28
29 package mir.changetracker;
30
31 import java.util.ArrayList;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.Date;
38 import java.util.Calendar;
39
40 /**
41  * Change tracker, tracks changes to a path based repository.
42  * All methods are thread-safe
43  */
44
45 public class ChangeTracker {
46   private final List changes = new ArrayList();
47   private Calendar calendar = Calendar.getInstance();
48
49   // to prevent memory issues with the number of recorded changes,
50   //   we'll adhere to this maximum
51   private static final int MAX_CHANGES = 10000;
52
53   /**
54    * Add a single change. A change is represented by the full path
55    * of the file involved.
56    */
57   public void addChange(String aPath, ChangeType aChangeType) {
58     synchronized (changes) {
59       changes.add(new Change(aPath, aChangeType, calendar.getTime()));
60     }
61   }
62
63   /**
64    * Add an array of changes. Each change is represented by the full path
65    * of the file involved.
66    */
67   public void addChanges(String[] aPaths, ChangeType aChangeType) {
68     addChanges(Arrays.asList(aPaths), aChangeType);
69   }
70
71   /**
72    * Adds a <code>Collection</code> of changes. Each change is represented by the
73    * full path of the file involved.
74    */
75   public void addChanges(Collection aChanges, ChangeType aChangeType) {
76     synchronized (changes) {
77       Iterator i = aChanges.iterator();
78       while (i.hasNext()) {
79         addChange((String) i.next(), aChangeType);
80       }
81     }
82   }
83
84   public class Change {
85     private ChangeType type;
86     private String path;
87     private Date date;
88
89     Change(String anAbsolutePath, ChangeType aType, Date aDate) {
90       type = aType;
91       path = anAbsolutePath;
92       date = aDate;
93     }
94
95     public String getPath() {
96       return path;
97     }
98
99     public ChangeType getType() {
100       return type;
101     }
102
103     public Date getDate() {
104       return date;
105     }
106
107   }
108
109   /**
110    * Returns a <code>Collection</code> of {@link Change}s within a base
111    *     path, and removes them from the tracker.
112    */
113   public Collection flushChanges(String aBasePath) {
114     return flushChanges(aBasePath, Collections.EMPTY_LIST);
115   }
116
117   /**
118    * Returns a <code>Collection</code> of {@link Change}s within a base
119    *     path, exluding a list of excluded paths, and removes them from
120    *     the tracker.
121    */
122   public Collection flushChanges(String aBasePath, Collection anExcludedPaths) {
123     synchronized (changes) {
124       Collection result = getChanges(aBasePath, anExcludedPaths);
125
126       removeChanges(result);
127
128       return result;
129     }
130   }
131
132   /**
133    * Remove specific changes from the change tracker.
134    *
135    * @param someChanges a <code>Collection</code> of changes represented by
136    * their full path in the form of a <code>String</code>
137    */
138   void removeChanges(Collection someChanges) {
139     synchronized (changes) {
140       changes.removeAll(someChanges);
141     }
142   }
143
144   /**
145    * Returns all changes within a base path
146    */
147   List getChanges(String aBasePath) {
148     synchronized (changes) {
149       List result = new ArrayList();
150
151       Iterator i = changes.iterator();
152       while (i.hasNext()) {
153         Change change = (Change) i.next();
154         if (change.getPath().startsWith(aBasePath)) {
155           result.add(change);
156         }
157       }
158
159       return result;
160     }
161   }
162
163   /**
164    * Gets all changes within a base path, but excluding some other paths
165    *
166    * @param aBasePath
167    * @param anExcludedPaths a collection of paths to exclude. may be <code>null</code>
168    *       to not exclude anything
169    */
170   Collection getChanges(String aBasePath, Collection anExcludedPaths) {
171     synchronized (changes) {
172       List result = getChanges(aBasePath);
173
174       if (anExcludedPaths != null) {
175         Iterator i = anExcludedPaths.iterator();
176         while (i.hasNext()) {
177           String excludedPath = (String) i.next();
178           List remove = new ArrayList();
179           Iterator j = result.iterator();
180           while (j.hasNext()) {
181             Change change = (Change) j.next();
182             if (change.getPath().startsWith(excludedPath)) {
183               remove.add(change);
184             }
185           }
186           result.removeAll(remove);
187         }
188       }
189
190       if (changes.size()>MAX_CHANGES) {
191         changes.clear();
192       }
193
194       return result;
195     }
196   }
197 }