001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.localhistory;
042:
043: import java.beans.PropertyChangeEvent;
044: import java.beans.PropertyChangeListener;
045: import java.io.File;
046: import java.text.SimpleDateFormat;
047: import java.util.Collections;
048: import java.util.Date;
049: import java.util.HashSet;
050: import java.util.Set;
051: import java.util.logging.Level;
052: import java.util.logging.Logger;
053: import java.util.regex.Pattern;
054: import org.netbeans.api.project.Project;
055: import org.netbeans.api.project.ProjectUtils;
056: import org.netbeans.api.project.SourceGroup;
057: import org.netbeans.api.project.Sources;
058: import org.netbeans.api.project.ui.OpenProjects;
059: import org.netbeans.modules.localhistory.store.LocalHistoryStore;
060: import org.netbeans.modules.localhistory.store.LocalHistoryStoreFactory;
061: import org.netbeans.modules.versioning.spi.VCSAnnotator;
062: import org.netbeans.modules.versioning.spi.VCSInterceptor;
063: import org.netbeans.modules.versioning.util.ListenersSupport;
064: import org.netbeans.modules.versioning.util.VersioningListener;
065: import org.openide.filesystems.FileObject;
066: import org.openide.filesystems.FileUtil;
067: import org.openide.util.RequestProcessor;
068: import org.openide.util.WeakListeners;
069:
070: /**
071: *
072: * A singleton Local History manager class, center of the Local History module.
073: * Use {@link #getInstance()} to get access to Local History module functionality.
074: * @author Tomas Stupka
075: */
076: public class LocalHistory {
077:
078: private static LocalHistory instance;
079: private VCSInterceptor vcsInterceptor;
080: private VCSAnnotator vcsAnnotator;
081: private LocalHistoryStore store;
082:
083: private ListenersSupport listenerSupport = new ListenersSupport(
084: this );
085:
086: private Set<File> userDefinedRoots;
087: private Set<File> roots = new HashSet<File>();
088:
089: private Pattern includeFiles = null;
090: private Pattern excludeFiles = null;
091:
092: // XXX hotfix - issue 119042
093: private final Pattern metadataPattern = Pattern.compile(".*\\"
094: + File.separatorChar + "((\\.|_)svn|.hg|CVS)(\\"
095: + File.separatorChar + ".*|$)");
096:
097: public final static Object EVENT_FILE_CREATED = new Object();
098: final static Object EVENT_PROJECTS_CHANGED = new Object();
099:
100: /** default logger for whole module */
101: public static final Logger LOG = Logger
102: .getLogger("org.netbeans.modules.localhistory"); // NOI18N
103:
104: public LocalHistory() {
105: String include = System
106: .getProperty("netbeans.localhistory.includeFiles");
107: if (include != null && !include.trim().equals("")) {
108: this .includeFiles = Pattern.compile(include);
109: }
110: String exclude = System
111: .getProperty("netbeans.localhistory.excludeFiles");
112: if (exclude != null && !exclude.trim().equals("")) {
113: this .excludeFiles = Pattern.compile(exclude);
114: }
115:
116: String rootPaths = System
117: .getProperty("netbeans.localhistory.historypath");
118: if (rootPaths == null || rootPaths.trim().equals("")) {
119: userDefinedRoots = Collections.EMPTY_SET;
120: } else {
121: String[] paths = rootPaths.split(";");
122: userDefinedRoots = new HashSet<File>(paths.length);
123: for (String root : paths) {
124: addRootFile(userDefinedRoots, new File(root));
125: }
126: }
127: }
128:
129: void init() {
130: getLocalHistoryStore().cleanUp(
131: LocalHistorySettings.getInstance().getTTLMillis());
132: RequestProcessor.getDefault().post(new Runnable() {
133: public void run() {
134: setRoots(OpenProjects.getDefault().getOpenProjects());
135: OpenProjects.getDefault().addPropertyChangeListener(
136: WeakListeners.propertyChange(
137: openProjectsListener, null));
138: }
139: });
140: }
141:
142: private void setRoots(Project[] projects) {
143: Set<File> newRoots = new HashSet<File>();
144: for (Project project : projects) {
145: Sources sources = ProjectUtils.getSources(project);
146: SourceGroup[] groups = sources
147: .getSourceGroups(Sources.TYPE_GENERIC);
148: for (SourceGroup group : groups) {
149: FileObject fo = group.getRootFolder();
150: File root = FileUtil.toFile(fo);
151: if (root == null) {
152: LOG.warning("source group" + group.getDisplayName()
153: + " returned null root folder");
154: } else {
155: addRootFile(newRoots, root);
156: }
157: }
158: File root = FileUtil.toFile(project.getProjectDirectory());
159: if (root == null) {
160: LOG.warning("project " + project.getProjectDirectory()
161: + " returned null root folder");
162: } else {
163: addRootFile(newRoots, root);
164: }
165: }
166: synchronized (roots) {
167: roots = newRoots;
168: }
169: fireFileEvent(EVENT_PROJECTS_CHANGED, null);
170: }
171:
172: private void addRootFile(Set<File> set, File file) {
173: if (file == null) {
174: return;
175: }
176: LOG.fine("adding root folder " + file);
177: set.add(file);
178: return;
179: }
180:
181: public static synchronized LocalHistory getInstance() {
182: if (instance == null) {
183: instance = new LocalHistory();
184: }
185: return instance;
186: }
187:
188: VCSInterceptor getVCSInterceptor() {
189: if (vcsInterceptor == null) {
190: vcsInterceptor = new LocalHistoryVCSInterceptor();
191: }
192: return vcsInterceptor;
193: }
194:
195: VCSAnnotator getVCSAnnotator() {
196: if (vcsAnnotator == null) {
197: vcsAnnotator = new LocalHistoryVCSAnnotator();
198: }
199: return vcsAnnotator;
200: }
201:
202: public LocalHistoryStore getLocalHistoryStore() {
203: if (store == null) {
204: store = LocalHistoryStoreFactory.getInstance()
205: .createLocalHistoryStorage();
206: }
207: return store;
208: }
209:
210: File isManagedByParent(File file) {
211: if (roots == null) {
212: // init not finnished yet
213: return file;
214: }
215: File parent = null;
216: while (file != null) {
217: synchronized (roots) {
218: if (roots.contains(file)
219: || userDefinedRoots.contains(file)) {
220: parent = file;
221: }
222: }
223: file = file.getParentFile();
224: }
225: return parent;
226: }
227:
228: boolean isManaged(File file) {
229: log("isManaged() " + file);
230:
231: if (file == null) {
232: return false;
233: }
234: String path = file.getAbsolutePath();
235: if (metadataPattern.matcher(path).matches()) {
236: return false;
237: }
238:
239: if (includeFiles != null) {
240: return includeFiles.matcher(path).matches();
241: }
242:
243: if (excludeFiles != null) {
244: return !excludeFiles.matcher(path).matches();
245: }
246:
247: return true;
248: }
249:
250: public void addVersioningListener(VersioningListener listener) {
251: listenerSupport.addListener(listener);
252: }
253:
254: public void removeVersioningListener(VersioningListener listener) {
255: listenerSupport.removeListener(listener);
256: }
257:
258: void fireFileEvent(Object id, File file) {
259: listenerSupport.fireVersioningEvent(id, new Object[] { file });
260: }
261:
262: PropertyChangeListener openProjectsListener = new PropertyChangeListener() {
263: public void propertyChange(PropertyChangeEvent evt) {
264: if (evt.getPropertyName().equals(
265: OpenProjects.PROPERTY_OPEN_PROJECTS)) {
266: final Project[] projects = (Project[]) evt
267: .getNewValue();
268: RequestProcessor.getDefault().post(new Runnable() {
269: public void run() {
270: setRoots(projects);
271: }
272: });
273: }
274: }
275: };
276:
277: public static void logCreate(File file, File storeFile, long ts,
278: String from, String to) {
279: if (!LOG.isLoggable(Level.FINE)) {
280: return;
281: }
282: StringBuffer sb = new StringBuffer();
283: sb.append("create");
284: sb.append('\t');
285: sb.append(file.getAbsolutePath());
286: sb.append('\t');
287: sb.append(storeFile.getAbsolutePath());
288: sb.append('\t');
289: sb.append(ts);
290: sb.append('\t');
291: sb.append(from);
292: sb.append('\t');
293: sb.append(to);
294: log(sb.toString());
295: }
296:
297: public static void logChange(File file, File storeFile, long ts) {
298: if (!LOG.isLoggable(Level.FINE)) {
299: return;
300: }
301: StringBuffer sb = new StringBuffer();
302: sb.append("change");
303: sb.append('\t');
304: sb.append(file.getAbsolutePath());
305: sb.append('\t');
306: sb.append(storeFile.getAbsolutePath());
307: sb.append('\t');
308: sb.append(ts);
309: log(sb.toString());
310: }
311:
312: public static void logDelete(File file, File storeFile, long ts) {
313: if (!LOG.isLoggable(Level.FINE)) {
314: return;
315: }
316: StringBuffer sb = new StringBuffer();
317: sb.append("delete");
318: sb.append('\t');
319: sb.append(file.getAbsolutePath());
320: sb.append('\t');
321: sb.append(storeFile.getAbsolutePath());
322: sb.append('\t');
323: sb.append(ts);
324: log(sb.toString());
325: }
326:
327: public static void logFile(String msg, File file) {
328: if (!LOG.isLoggable(Level.FINE)) {
329: return;
330: }
331: StringBuffer sb = new StringBuffer();
332: sb.append(msg);
333: sb.append('\t');
334: sb.append(file.getAbsolutePath());
335: log(sb.toString());
336: }
337:
338: public static void log(String msg) {
339: if (!LOG.isLoggable(Level.FINE)) {
340: return;
341: }
342: StringBuffer sb = new StringBuffer();
343: SimpleDateFormat defaultFormat = new SimpleDateFormat(
344: "dd-MM-yyyy:HH-mm-ss.S");
345: sb.append(defaultFormat.format(new Date(System
346: .currentTimeMillis())));
347: sb.append(":");
348: sb.append(msg);
349: sb.append('\t');
350: sb.append(Thread.currentThread().getName());
351: LocalHistory.LOG.fine(sb.toString()); // NOI18N
352: }
353:
354: }
|