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.node.TreeNode;
036: import fr.loria.ecoo.so6.xml.util.XmlUtil;
037:
038: import org.libresource.so6.core.StateMonitoring;
039: import org.libresource.so6.core.WsConnection;
040: import org.libresource.so6.core.command.Command;
041: import org.libresource.so6.core.command.NeutralCommand;
042: import org.libresource.so6.core.command.fs.AddBinaryFile;
043: import org.libresource.so6.core.command.fs.AddDir;
044: import org.libresource.so6.core.command.fs.Remove;
045: import org.libresource.so6.core.command.fs.Rename;
046: import org.libresource.so6.core.command.fs.UpdateBinaryFile;
047: import org.libresource.so6.core.command.text.AddBlock;
048: import org.libresource.so6.core.command.text.AddTxtFile;
049: import org.libresource.so6.core.command.text.DelBlock;
050: import org.libresource.so6.core.command.xml.AddXmlFile;
051: import org.libresource.so6.core.command.xml.DeleteAttribute;
052: import org.libresource.so6.core.command.xml.DeleteNode;
053: import org.libresource.so6.core.command.xml.InsertAttribute;
054: import org.libresource.so6.core.command.xml.InsertNode;
055: import org.libresource.so6.core.command.xml.UpdateAttribute;
056: import org.libresource.so6.core.engine.util.Base64;
057: import org.libresource.so6.core.engine.util.FileUtils;
058:
059: import org.xml.sax.Attributes;
060: import org.xml.sax.SAXException;
061: import org.xml.sax.helpers.DefaultHandler;
062:
063: import java.io.File;
064: import java.io.FileOutputStream;
065: import java.io.IOException;
066: import java.io.InputStream;
067: import java.io.OutputStreamWriter;
068: import java.io.UnsupportedEncodingException;
069:
070: import java.util.ArrayList;
071: import java.util.Collection;
072: import java.util.ListIterator;
073: import java.util.Vector;
074: import java.util.logging.Logger;
075:
076: import javax.xml.parsers.SAXParser;
077: import javax.xml.parsers.SAXParserFactory;
078:
079: /**
080: * @author molli
081: */
082: public class PatchFile {
083: private String patchfile;
084: private OpVector locals;
085: private File baseAttachDir;
086:
087: public PatchFile(String patchfile) {
088: this .patchfile = patchfile;
089: }
090:
091: public static void makePatch(OpVector opv, OutputStreamWriter osw,
092: Vector patchFilter, long fromTicket, long toTicket,
093: String origine, String comment) throws Exception {
094: osw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
095: osw.write("<patch>");
096: osw.write("<name>"
097: + Base64.encodeBytes(origine.getBytes("UTF-8"))
098: + "</name>");
099: osw.write("<begin>" + fromTicket + "</begin>");
100: osw.write("<end>" + toTicket + "</end>");
101:
102: if (comment != null) {
103: osw.write("<comment>"
104: + Base64.encodeBytes(comment.getBytes("UTF-8"))
105: + "</comment>");
106: }
107:
108: long ticket = fromTicket;
109: ListIterator iterator = opv.getCommands();
110: Command cmd = null;
111:
112: while ((cmd = (Command) iterator.next()) != null) {
113: if (cmd instanceof NeutralCommand) {
114: continue;
115: }
116:
117: if ((patchFilter != null)
118: && !patchFilter.contains(cmd.getPath())) {
119: continue;
120: }
121:
122: StateMonitoring.getInstance().setXMLMonitoringState(
123: fromTicket, toTicket, ticket, "");
124: cmd.setTicket(ticket);
125: ticket++;
126: iterator.set(cmd);
127: osw.write("<command>");
128: osw
129: .write("<class>" + cmd.getClass().getName()
130: + "</class>");
131: cmd.toXML(osw);
132: osw.write("</command>");
133: }
134:
135: osw.write("</patch>");
136: osw.flush();
137: }
138:
139: public void patch(String dir, DBType dbt) throws Exception {
140: baseAttachDir = FileUtils.createTmpDir();
141:
142: SAXParserFactory factory = SAXParserFactory.newInstance();
143: SAXParser saxParser = factory.newSAXParser();
144: saxParser.parse(new File(patchfile), new ApplyPatchHandler(dir,
145: dbt));
146: dbt.save();
147: }
148:
149: public void merge(WsConnection ws, OpVector locals)
150: throws Exception {
151: baseAttachDir = new File(ws.getMergedAttachPath());
152:
153: SAXParserFactory factory = SAXParserFactory.newInstance();
154: SAXParser saxParser = factory.newSAXParser();
155: saxParser.parse(new File(patchfile), new MergePatchHandler(ws,
156: locals));
157: ws.getDBType().save();
158: }
159:
160: public void buildOpVector(OpVector vector, String baseAttachDir,
161: Collection filter) throws Exception {
162: this .baseAttachDir = new File(baseAttachDir);
163:
164: SAXParserFactory factory = SAXParserFactory.newInstance();
165: SAXParser saxParser = factory.newSAXParser();
166: saxParser.parse(new File(patchfile),
167: new BuildOpVectorPatchHandler(vector, filter));
168: }
169:
170: public void buildOpVector(InputStream in, OpVector vector,
171: String baseAttachDir, Collection filter) throws Exception {
172: this .baseAttachDir = new File(baseAttachDir);
173:
174: SAXParserFactory factory = SAXParserFactory.newInstance();
175: SAXParser saxParser = factory.newSAXParser();
176: saxParser.parse(in, new BuildOpVectorPatchHandler(vector,
177: filter));
178: }
179:
180: private boolean checkCommandType(String cmdFromPatch, String realCmd) {
181: return cmdFromPatch.substring(cmdFromPatch.lastIndexOf("."))
182: .equals(realCmd.substring(realCmd.lastIndexOf(".")));
183: }
184:
185: public class ApplyPatchHandler extends PatchHandler {
186: private String dir;
187: private DBType dbt;
188:
189: ApplyPatchHandler(String dir, DBType dbt) {
190: this .dir = dir;
191: this .dbt = dbt;
192: }
193:
194: public void doit(Command cmd) {
195: try {
196: StateMonitoring.getInstance().setXMLMonitoringState(
197: fromTicket, toTicket, ticket,
198: "Apply patch(" + cmd + ")");
199: Logger.getLogger("ui.log").info("executing:" + cmd);
200: cmd.execute(dir, dbt);
201: } catch (Exception e) {
202: e.printStackTrace();
203: throw new RuntimeException(e);
204: }
205: }
206: }
207:
208: public class MergePatchHandler extends PatchHandler {
209: private OpVector locals;
210: private WsConnection ws;
211:
212: MergePatchHandler(WsConnection ws, OpVector locals) {
213: this .locals = locals;
214: this .ws = ws;
215: }
216:
217: public void doit(Command cmd) {
218: try {
219: StateMonitoring.getInstance().setXMLMonitoringState(
220: fromTicket, toTicket, ticket,
221: "Merging patch(" + cmd + ")");
222: ws.merge(cmd, locals);
223: } catch (Exception e) {
224: e.printStackTrace();
225: throw new RuntimeException(e);
226: }
227: }
228: }
229:
230: public class BuildOpVectorPatchHandler extends PatchHandler {
231: private OpVector vector;
232: private WsConnection ws;
233: private Collection filter;
234:
235: BuildOpVectorPatchHandler(OpVector vector, Collection filter) {
236: this .vector = vector;
237: this .filter = filter;
238: }
239:
240: public void setFilter(Collection filter) {
241: this .filter = filter;
242: }
243:
244: public void doit(Command cmd) {
245: try {
246: if (filter == null) {
247: vector.add(cmd);
248: } else {
249: if (filter.contains(cmd.getPath())) {
250: vector.add(cmd);
251: }
252: }
253: } catch (Exception e) {
254: e.printStackTrace();
255: throw new RuntimeException(e);
256: }
257: }
258: }
259:
260: abstract class PatchHandler extends DefaultHandler {
261: private String tag;
262: private StringBuffer buffer;
263: private FileOutputStream fos;
264: private Command cmd;
265: private ArrayList alist;
266:
267: // for patch
268: protected long fromTicket = -1;
269: protected long toTicket = -1;
270: private String classname;
271: private String filename;
272: protected long ticket;
273: private String path;
274: private String newPath;
275: private String wsName;
276: private Long time;
277:
278: // For txt cmds
279: private Integer insertPoint;
280: private Integer deletePoint;
281:
282: // For xml cmds
283: private String xmlNodePath;
284: private String xmlAttributeName;
285: private String xmlAttributeValue;
286: private String xmlOldAttributeValue;
287: private String attributeName;
288: private TreeNode treeNode;
289:
290: PatchHandler() {
291: }
292:
293: public void characters(char[] buff, int offset, int len)
294: throws SAXException {
295: // TODO improve base64 use
296: if (tag.equals(Command.ATTACHEMENT)) {
297: buffer.append(buff, offset, len);
298:
299: int pos = -1;
300:
301: while ((pos = buffer.indexOf("\n")) != -1) {
302: while ((pos = buffer.indexOf("\n")) != -1) {
303: buffer.deleteCharAt(pos);
304:
305: //System.out.println("Remove invalide base64 char");
306: }
307:
308: int nbCharToDecode = (buffer.length() / 4);
309:
310: try {
311: fos.write(Base64.decode(buffer.substring(0,
312: nbCharToDecode * 4)));
313: buffer.delete(0, nbCharToDecode * 4);
314: } catch (IOException e) {
315: e.printStackTrace();
316: throw new RuntimeException(e);
317: } catch (RuntimeException e) {
318: e.printStackTrace();
319: throw e;
320: }
321: }
322: } else {
323: buffer.append(buff, offset, len);
324: }
325: }
326:
327: public void endElement(String namespaceuri, String sname,
328: String qname) throws SAXException {
329: if (qname.equals(Command.ATTACHEMENT)) {
330: try {
331: fos.write(Base64.decode(buffer.toString()));
332: fos.flush();
333: fos.close();
334: } catch (IOException e) {
335: throw new RuntimeException(e);
336: }
337: }
338:
339: if (qname.equals("begin")) {
340: fromTicket = Long.parseLong(buffer.toString());
341: }
342:
343: if (qname.equals("end")) {
344: toTicket = Long.parseLong(buffer.toString());
345: }
346:
347: if (qname.equals("class")) {
348: classname = buffer.toString();
349: }
350:
351: if (qname.equals("ticket")) {
352: ticket = Long.parseLong(buffer.toString());
353: }
354:
355: if (qname.equals("from")) {
356: try {
357: wsName = new String(Base64
358: .decode(buffer.toString()), "UTF-8");
359: } catch (UnsupportedEncodingException e) {
360: // TODO: handle exception
361: }
362: }
363:
364: if (qname.equals("newpath")) {
365: try {
366: newPath = new String(Base64.decode(buffer
367: .toString()), "UTF-8");
368: } catch (UnsupportedEncodingException e) {
369: // TODO: handle exception
370: }
371: }
372:
373: if (qname.equals("time")) {
374: time = new Long(buffer.toString());
375: }
376:
377: if (qname.equals("path")) {
378: try {
379: path = new String(Base64.decode(buffer.toString()),
380: "UTF-8");
381: } catch (UnsupportedEncodingException e) {
382: // TODO: handle exception
383: }
384: }
385:
386: if (qname.equals("insertpoint")) {
387: insertPoint = new Integer(buffer.toString());
388: }
389:
390: if (qname.equals("deletepoint")) {
391: deletePoint = new Integer(buffer.toString());
392: }
393:
394: if (qname.equals("line")) {
395: try {
396: String s = new String(Base64.decode(buffer
397: .toString()), "UTF-8");
398: alist.add(s);
399: } catch (UnsupportedEncodingException e) {
400: // TODO: handle exception
401: }
402: }
403:
404: // For XML
405: if (qname.equals("treeNode")) {
406: int pos = -1;
407:
408: while ((pos = buffer.indexOf("\n")) != -1) {
409: buffer.deleteCharAt(pos);
410:
411: //System.out.println("Remove invalide base64 char");
412: }
413:
414: try {
415: treeNode = XmlUtil.importNode(buffer.toString());
416: } catch (Exception e) {
417: throw new RuntimeException(e);
418: }
419: }
420:
421: if (qname.equals("xmlNodePath")) {
422: xmlNodePath = buffer.toString();
423: }
424:
425: if (qname.equals("xmlAttributeName")) {
426: try {
427: xmlAttributeName = new String(Base64.decode(buffer
428: .toString()), "UTF-8");
429: } catch (UnsupportedEncodingException e) {
430: // TODO Auto-generated catch block
431: }
432: }
433:
434: if (qname.equals("xmlAttributeValue")) {
435: try {
436: xmlAttributeValue = new String(Base64.decode(buffer
437: .toString()), "UTF-8");
438: } catch (UnsupportedEncodingException e) {
439: // TODO Auto-generated catch block
440: }
441: }
442:
443: if (qname.equals("xmlOldAttributeValue")) {
444: try {
445: xmlOldAttributeValue = new String(Base64
446: .decode(buffer.toString()), "UTF-8");
447: } catch (UnsupportedEncodingException e) {
448: // TODO Auto-generated catch block
449: }
450: }
451:
452: // Build real command
453: if (qname.equals("command")) {
454: if (checkCommandType(classname, Rename.class.getName())) {
455: cmd = new Rename(ticket, path, wsName, time
456: .longValue(), false, null, newPath);
457: }
458:
459: if (checkCommandType(classname, AddDir.class.getName())) {
460: cmd = new AddDir(ticket, path, wsName, time
461: .longValue(), false, null);
462: }
463:
464: if (checkCommandType(classname, AddTxtFile.class
465: .getName())) {
466: cmd = new AddTxtFile(ticket, path, wsName, time
467: .longValue(), false, filename);
468: }
469:
470: if (checkCommandType(classname, Remove.class.getName())) {
471: cmd = new Remove(ticket, path, wsName, time
472: .longValue(), false, null);
473: }
474:
475: if (checkCommandType(classname, AddBlock.class
476: .getName())) {
477: cmd = new AddBlock(ticket, path, wsName, time
478: .longValue(), false,
479: insertPoint.intValue(), alist);
480: }
481:
482: if (checkCommandType(classname, DelBlock.class
483: .getName())) {
484: cmd = new DelBlock(ticket, path, wsName, time
485: .longValue(), false,
486: deletePoint.intValue(), alist);
487: }
488:
489: if (checkCommandType(classname, AddBinaryFile.class
490: .getName())) {
491: cmd = new AddBinaryFile(ticket, path, wsName, time
492: .longValue(), false, filename);
493: }
494:
495: if (checkCommandType(classname, UpdateBinaryFile.class
496: .getName())) {
497: cmd = new UpdateBinaryFile(ticket, path, wsName,
498: time.longValue(), false, filename);
499: }
500:
501: // XML
502: if (checkCommandType(classname, AddXmlFile.class
503: .getName())) {
504: cmd = new AddXmlFile(ticket, path, wsName, time
505: .longValue(), filename);
506: }
507:
508: if (checkCommandType(classname, DeleteAttribute.class
509: .getName())) {
510: cmd = new DeleteAttribute(ticket, path, wsName,
511: time.longValue(), xmlNodePath,
512: xmlAttributeName);
513: }
514:
515: if (checkCommandType(classname, DeleteNode.class
516: .getName())) {
517: cmd = new DeleteNode(ticket, path, wsName, time
518: .longValue(), xmlNodePath, treeNode);
519: }
520:
521: if (checkCommandType(classname, InsertAttribute.class
522: .getName())) {
523: cmd = new InsertAttribute(ticket, path, wsName,
524: time.longValue(), xmlNodePath,
525: xmlAttributeName, xmlAttributeValue);
526: }
527:
528: if (checkCommandType(classname, InsertNode.class
529: .getName())) {
530: cmd = new InsertNode(ticket, path, wsName, time
531: .longValue(), xmlNodePath, treeNode);
532: }
533:
534: if (checkCommandType(classname, UpdateAttribute.class
535: .getName())) {
536: cmd = new UpdateAttribute(ticket, path, wsName,
537: time.longValue(), xmlNodePath,
538: xmlAttributeName, xmlOldAttributeValue,
539: xmlAttributeValue);
540: }
541:
542: if (cmd != null) {
543: doit(cmd);
544: cmd = null;
545: alist = null;
546: } else {
547: throw new RuntimeException("Class " + classname
548: + " unmanaged");
549: }
550: }
551: }
552:
553: public abstract void doit(Command cmd);
554:
555: public void startElement(String namespaceuri, String sname,
556: String qname, Attributes attr) throws SAXException {
557: tag = qname;
558: buffer = new StringBuffer();
559:
560: if (qname.equals("linesToAdd")
561: || qname.equals("linesToRemove")) {
562: alist = new ArrayList();
563: }
564:
565: if (qname.equals(Command.ATTACHEMENT)) {
566: try {
567: File f = File.createTempFile("attach", null,
568: baseAttachDir);
569: fos = new FileOutputStream(f);
570: filename = f.getPath();
571: } catch (IOException e) {
572: throw new RuntimeException(e);
573: }
574: }
575: }
576: }
577: }
|