001: /**
002: * LibreSource
003: * Copyright (C) 2004-2008 Artenum SARL / INRIA
004: * http://www.libresource.org - contact@artenum.com
005: *
006: * This file is part of the LibreSource software,
007: * which can be used and distributed under license conditions.
008: * The license conditions are provided in the LICENSE.TXT file
009: * at the root path of the packaging that enclose this file.
010: * More information can be found at
011: * - http://dev.libresource.org/home/license
012: *
013: * Initial authors :
014: *
015: * Guillaume Bort / INRIA
016: * Francois Charoy / Universite Nancy 2
017: * Julien Forest / Artenum
018: * Claude Godart / Universite Henry Poincare
019: * Florent Jouille / INRIA
020: * Sebastien Jourdain / INRIA / Artenum
021: * Yves Lerumeur / Artenum
022: * Pascal Molli / Universite Henry Poincare
023: * Gerald Oster / INRIA
024: * Mariarosa Penzi / Artenum
025: * Gerard Sookahet / Artenum
026: * Raphael Tani / INRIA
027: *
028: * Contributors :
029: *
030: * Stephane Bagnier / Artenum
031: * Amadou Dia / Artenum-IUP Blois
032: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
033: */package org.libresource.so6.core.engine;
034:
035: import fr.loria.ecoo.so6.xml.xydiff.XyDiff;
036:
037: import jlibdiff.Diff;
038: import jlibdiff.Hunk;
039: import jlibdiff.HunkAdd;
040: import jlibdiff.HunkChange;
041: import jlibdiff.HunkDel;
042:
043: import org.libresource.so6.core.FileLockedException;
044: import org.libresource.so6.core.StateMonitoring;
045: import org.libresource.so6.core.Workspace;
046: import org.libresource.so6.core.WsConnection;
047: import org.libresource.so6.core.command.Command;
048: import org.libresource.so6.core.command.fs.AddBinaryFile;
049: import org.libresource.so6.core.command.fs.AddDir;
050: import org.libresource.so6.core.command.fs.Remove;
051: import org.libresource.so6.core.command.fs.UpdateBinaryFile;
052: import org.libresource.so6.core.command.text.AddBlock;
053: import org.libresource.so6.core.command.text.AddTxtFile;
054: import org.libresource.so6.core.command.text.DelBlock;
055: import org.libresource.so6.core.command.xml.AddXmlFile;
056: import org.libresource.so6.core.engine.util.FileUtils;
057: import org.libresource.so6.core.engine.util.XmlUtil;
058:
059: import java.io.File;
060: import java.io.IOException;
061:
062: import java.sql.SQLException;
063:
064: import java.util.ArrayList;
065: import java.util.Collection;
066: import java.util.Collections;
067: import java.util.Iterator;
068: import java.util.logging.Logger;
069:
070: /**
071: * FileParser : Detect file modification...
072: *
073: * Responsablity : - This is a simple modification detector for a file system.
074: * computes the vector of operations that make the transition from the last
075: * state (stored in a local db) to the actual state. - FileParser has the
076: * responsability to manage DBMarks (update it during synchronization action)
077: *
078: * Collaboration: - give its operation to the operation integrator component.
079: *
080: * @author molli
081: */
082: public class FileParser {
083: private WsConnection ws;
084: private FilterIgnoreFile fif;
085: private int walked = 0;
086: private long size = 0;
087: private ArrayList walkedlist = new ArrayList();
088: private WsConnection workspace;
089: private int nbFileToWalk;
090:
091: public FileParser(WsConnection ws) throws Exception {
092: this .ws = ws;
093:
094: String dir = FileUtils.createTmpDir().getPath();
095: File f = new File(dir);
096: f.mkdir();
097: }
098:
099: public WsConnection getWorkspace() {
100: return workspace;
101: }
102:
103: public void setWorkspace(WsConnection workspace) {
104: this .workspace = workspace;
105: }
106:
107: private String getRelPath(String path) {
108: String p1 = (new File(ws.getPath())).getAbsolutePath();
109: String p2 = ((new File(path))).getAbsolutePath();
110: assert (p2.startsWith(p1)) : "(" + p2
111: + ") does not start with (" + p1 + ")";
112:
113: //System.out.println("ws="+p1 + "| path="+p2+"|");
114: // +1 to remove the first / of the command path
115: if (p1.equals(p2)) {
116: return "";
117: } else {
118: if (p2.startsWith(p1)) {
119: return p2.substring(p1.length() + 1).replaceAll("\\\\",
120: "/");
121: }
122:
123: return path.replaceAll("\\\\", "/");
124: }
125: }
126:
127: public void compute(OpVector log) throws Exception {
128: StateMonitoring.getInstance().setXMLMonitoringStartSubCall(4,
129: "");
130: StateMonitoring.getInstance().setXMLMonitoringStartSubCall(1,
131: "Check number of file to walk");
132: ws.getDBType().updateFromWalk(ws.getPath(),
133: ws.getXmlAutoDetection());
134: nbFileToWalk = ws.getDBType().getNbEntry();
135:
136: ArrayList walkedlist = new ArrayList();
137: fif = new FilterIgnoreFile(ws);
138: StateMonitoring.getInstance().setXMLMonitoringEndSubCall();
139: StateMonitoring.getInstance().setXMLMonitoringStartSubCall(1,
140: "Check local operation");
141:
142: File root = new File(ws.getPath());
143:
144: // check il important data is locked
145: File[] children = root.listFiles();
146:
147: if (FileUtils.isLocked(ws.getRefCopyPath())) {
148: throw new FileLockedException(ws.getRefCopyPath());
149: }
150:
151: for (int i = 0; i < children.length; i++) {
152: if (children[i].getName().equals(Workspace.SO6PREFIX)) {
153: continue;
154: }
155:
156: if (FileUtils.isLocked(children[i])) {
157: throw new FileLockedException(children[i]);
158: }
159: }
160:
161: // Start walking to detect local op
162: this .walk(root, walkedlist, log);
163: StateMonitoring.getInstance().setXMLMonitoringEndSubCall();
164: StateMonitoring.getInstance().setXMLMonitoringStartSubCall(1,
165: "Detecting local remove");
166:
167: ArrayList removed = detectRemoved(walkedlist);
168:
169: for (int i = 0; i < removed.size(); i++) {
170: Command cmd = new Remove((String) removed.get(i), ws);
171: log.add(cmd);
172: }
173:
174: StateMonitoring.getInstance().setXMLMonitoringEndSubCall();
175: StateMonitoring.getInstance().setXMLMonitoringEndSubCall();
176: }
177:
178: private ArrayList detectRemoved(ArrayList l) {
179: ArrayList result = new ArrayList();
180: Iterator li = ws.getRefCopy().getElements();
181: int to = ws.getRefCopy().getDBType().getNbEntry();
182: int current = 0;
183:
184: while (li.hasNext()) {
185: current++;
186: StateMonitoring.getInstance().setXMLMonitoringState(0, to,
187: current, "");
188:
189: String path = (String) li.next();
190:
191: if (!(l.contains(path))) {
192: result.add(path);
193: StateMonitoring.getInstance().setXMLMonitoringComment(
194: "Find local remove: " + path);
195: }
196: }
197:
198: Collections.sort(result);
199: Collections.reverse(result);
200:
201: return result;
202: }
203:
204: public void diff(String path, OpVector log) throws IOException,
205: SQLException, Exception {
206: if (new File(ws.getPath() + File.separator + path)
207: .isDirectory()) {
208: return;
209: }
210:
211: if (!(ws.getRefCopy().exists(path))) {
212: // New File
213: throw new RuntimeException("Should never happen");
214: }
215:
216: // File previously synchronized
217: String lastFile = ws.getRefCopy().getPath(path);
218: String newFile = ws.getPath() + File.separator + path;
219:
220: if (ws.getRefCopy().getType(path) == DBType.TYPE_FILE_XML) {
221: // Manage XML
222: XyDiff d = new XyDiff(lastFile, newFile);
223: Collection cmds = XmlUtil.convertToSo6Commands(ws, path, d
224: .diff().getXMLCommand());
225:
226: //System.out.println("--> xml cmds");
227: for (Iterator i = cmds.iterator(); i.hasNext();) {
228: Command cmd = (Command) i.next();
229:
230: //System.out.println(cmd.toString());
231: log.add(cmd);
232: }
233:
234: //System.out.println("<-- xml cmds");
235: } else {
236: // Manage Txt
237: Diff d = new Diff();
238: d.diffFile(lastFile, newFile);
239:
240: for (Iterator i = d.iterator(); i.hasNext();) {
241: Hunk h = (Hunk) i.next();
242:
243: if (h instanceof HunkAdd) {
244: HunkAdd ha = (HunkAdd) h;
245: Command cmd = new AddBlock(path, ws, (HunkAdd) h);
246: log.add(cmd);
247: } else if (h instanceof HunkDel) {
248: HunkDel hd = (HunkDel) h;
249: Command cmd = new DelBlock(path, ws, (HunkDel) h);
250: log.add(cmd);
251: } else if (h instanceof HunkChange) {
252: throw new RuntimeException(
253: "HunkChange might not be detected, check the jlibdiff configuration");
254:
255: // HunkChange hc = (HunkChange) h;
256: // Logger.getLogger("fileparser.log").info(
257: // "hunkChange(ld1:" + hc.getLD1() + ",lf1:" + hc.getLF1() +
258: // ",ld2:" + hc.getLD2() + ",lf2:" + hc.getLF2() + ")");
259: // HunkDel hd = hc.getHunkDel();
260: // HunkAdd ha = hc.getHunkAdd();
261: // Command cmd = new DelBlock(path, ws, ((HunkChange)
262: // h).getHunkDel());
263: // log.add(cmd);
264: // cmd = new AddBlock(path, ws, ((HunkChange)
265: // h).getHunkAdd());
266: // log.add(cmd);
267: }
268: }
269: }
270:
271: System.gc();
272: } // traverse the file system starting from f // and build the vector of
273:
274: // operations
275: private void walk(File f, ArrayList wl, OpVector log)
276: throws java.io.IOException, SQLException, Exception {
277: if (!(f.exists())) {
278: throw new java.io.IOException(f.getPath() + " not exists");
279: }
280:
281: if (!(f.isDirectory()) && !(f.isFile())) {
282: Logger.getLogger("ui.log").warning(
283: "unmanaged file type:" + f.getPath());
284:
285: return;
286: }
287:
288: // ignore dataFiles !!
289: if (f.getName().equals(Workspace.SO6PREFIX)) {
290: return;
291: }
292:
293: String relpath = getRelPath(f.getPath()); // get the relative path...
294: Logger.getLogger("ui.log").severe("examining:" + relpath);
295: wl.add(relpath);
296: walked++;
297: size = size + f.length();
298:
299: // monitoring xml
300: StateMonitoring.getInstance().setXMLMonitoringState(0,
301: nbFileToWalk, walked, "walked... (" + relpath + ")");
302:
303: boolean filePreviouslySynchronized = ws.getRefCopy().exists(
304: relpath);
305: boolean typeHasChangedDirToFile = ws.getDBType()
306: .typeHasChanged(relpath, f);
307: boolean fileTypeHasChanged = ws.getRefCopy().getType(relpath) != ws
308: .getDBType().getType(relpath);
309: int fileType = -1;
310:
311: if ((!filePreviouslySynchronized) || typeHasChangedDirToFile
312: || fileTypeHasChanged) { // new
313:
314: // file...
315: // or
316: // type
317: // has
318: // changed
319: if (filePreviouslySynchronized
320: && (typeHasChangedDirToFile || fileTypeHasChanged)) { // Type
321:
322: // has
323: // changed
324: log.add(new Remove(relpath, ws));
325:
326: if (typeHasChangedDirToFile) {
327: ws.getDBType().remove(relpath);
328: } else {
329: fileType = ws.getDBType().getType(relpath);
330: ws.getDBType().remove(relpath);
331: }
332: }
333:
334: if (fileType == -1) {
335: // Try to set it from the local db type
336: fileType = ws.getDBType().getType(relpath);
337: }
338:
339: if (fileType == -1) {
340: // compute the type
341: fileType = ws.getDBType().computeType(f,
342: ws.getXmlAutoDetection());
343: }
344:
345: switch (fileType) {
346: case DBType.TYPE_DIR:
347: log.add(new AddDir(relpath, ws));
348: ws.getDBType().add(relpath, DBType.TYPE_DIR);
349:
350: break;
351:
352: case DBType.TYPE_FILE_BIN:
353: log.add(new AddBinaryFile(relpath, ws, new File(ws
354: .getAttachFilePath())));
355: ws.getDBType().add(relpath, DBType.TYPE_FILE_BIN);
356:
357: break;
358:
359: case DBType.TYPE_FILE_TXT:
360: log.add(new AddTxtFile(relpath, ws, new File(ws
361: .getAttachFilePath())));
362: ws.getDBType().add(relpath, DBType.TYPE_FILE_TXT);
363:
364: break;
365:
366: case DBType.TYPE_FILE_XML:
367: ws.getDBType().add(relpath, DBType.TYPE_FILE_XML);
368:
369: // Manage Xml
370: String newFile = ws.getPath() + File.separator
371: + relpath;
372:
373: // normalize the document
374: //XmlUtil.normalizeDocument(newFile);
375: log.add(new AddXmlFile(relpath, ws, new File(ws
376: .getAttachFilePath())));
377:
378: break;
379:
380: default:
381: throw new Exception("unmanaged db type: " + fileType
382: + " file: " + f.getAbsolutePath());
383: }
384: } else { // previously synchronized file
385:
386: // Type has not changed, just update...
387: switch (ws.getRefCopy().getType(relpath)) {
388: case DBType.TYPE_DIR:
389: break;
390:
391: case DBType.TYPE_FILE_BIN:
392:
393: if (!FileUtils.compareBinFile(f, new File(ws
394: .getRefCopy().getPath(relpath)))) {
395: log.add(new UpdateBinaryFile(relpath, ws, new File(
396: ws.getAttachFilePath())));
397: }
398:
399: break;
400:
401: case DBType.TYPE_FILE_TXT:
402: case DBType.TYPE_FILE_XML:
403:
404: if (!FileUtils.compareBinFile(f, new File(ws
405: .getRefCopy().getPath(relpath)))) {
406: this .diff(relpath, log);
407: }
408:
409: break;
410:
411: default:
412: throw new Exception("Unmanaged type : "
413: + ws.getRefCopy().getType(relpath));
414: }
415: }
416:
417: // recurse walking to seek children changes
418: if (f.isDirectory()) { // recurse walking to detect new children
419:
420: File[] subs = f.listFiles(fif); // filtered !!
421:
422: for (int i = 0; i < subs.length; i++) {
423: walk(subs[i], wl, log);
424: }
425: }
426: }
427: }
|