View Javadoc
1   package org.vaadin.aceeditor;
2   
3   import java.util.Collections;
4   import java.util.LinkedList;
5   import java.util.List;
6   import java.util.Map;
7   import java.util.Set;
8   
9   import name.fraser.neil.plaintext.diff_match_patch;
10  import name.fraser.neil.plaintext.diff_match_patch.Patch;
11  
12  import org.vaadin.aceeditor.client.AceAnnotation.MarkerAnnotation;
13  import org.vaadin.aceeditor.client.AceAnnotation.RowAnnotation;
14  import org.vaadin.aceeditor.client.AceDoc;
15  import org.vaadin.aceeditor.client.AceMarker;
16  import org.vaadin.aceeditor.client.MarkerSetDiff;
17  import org.vaadin.aceeditor.client.SetDiff;
18  import org.vaadin.aceeditor.client.TransportDiff;
19  import org.vaadin.aceeditor.client.TransportDiff.TransportSetDiffForMarkerAnnotations;
20  import org.vaadin.aceeditor.client.TransportDiff.TransportSetDiffForRowAnnotations;
21  import org.vaadin.aceeditor.client.TransportDoc.TransportMarkerAnnotation;
22  import org.vaadin.aceeditor.client.TransportDoc.TransportRowAnnotation;
23  
24  
25  
26  
27  public class ServerSideDocDiff {
28  	
29  	// We could use ThreadLocal but that causes a (valid) complaint
30  	// of memory leak by Tomcat. Creating a new diff_match_patch every
31  	// time (in getDmp()) fixes that. The creation is not a heavy operation so this it's ok.
32  	/*
33  	private static final ThreadLocal <diff_match_patch> dmp = 
34  	         new ThreadLocal <diff_match_patch> () {
35  	             @Override protected diff_match_patch initialValue() {
36  	                 return new diff_match_patch();
37  	         }
38  	     };
39  	*/
40  	
41  	private static diff_match_patch getDmp() {
42  		return new diff_match_patch();
43  	}
44  	
45  	private final LinkedList<Patch> patches;
46  	private final MarkerSetDiff markerSetDiff;
47  	private final SetDiff<RowAnnotation,TransportRowAnnotation> rowAnnDiff;
48  	private final SetDiff<MarkerAnnotation,TransportMarkerAnnotation> markerAnnDiff;
49  	
50  	public static ServerSideDocDiff diff(AceDoc doc1, AceDoc doc2) {
51  		LinkedList<Patch> patches = getDmp().patch_make(doc1.getText(), doc2.getText());
52  		MarkerSetDiff msd = MarkerSetDiff.diff(doc1.getMarkers(), doc2.getMarkers(), doc2.getText());
53  		SetDiff<RowAnnotation,TransportRowAnnotation> rowAnnDiff =
54  				diffRA(doc1.getRowAnnotations(), doc2.getRowAnnotations());		
55  		SetDiff<MarkerAnnotation,TransportMarkerAnnotation> markerAnnDiff =
56  				diffMA(doc1.getMarkerAnnotations(), doc2.getMarkerAnnotations());
57  		return new ServerSideDocDiff(patches, msd, rowAnnDiff, markerAnnDiff);
58  	}
59  	
60  	public static ServerSideDocDiff diff(String text1, String text2) {
61  		LinkedList<Patch> patches = getDmp().patch_make(text1, text2);
62  		return new ServerSideDocDiff(patches);
63  	}
64  	
65  
66  	// XXX Unnecessary copy-pasting
67  	private static SetDiff<MarkerAnnotation, TransportMarkerAnnotation> diffMA(
68  			Set<MarkerAnnotation> anns1,
69  			Set<MarkerAnnotation> anns2) {
70  		if (anns2 == null && anns1 != null) {
71  			return null;
72  		}
73  		if (anns1==null) {
74  			anns1 = Collections.emptySet();
75  		}
76  		if (anns2==null) {
77  			anns2 = Collections.emptySet();
78  		}
79  		return new SetDiff.Differ<MarkerAnnotation,TransportMarkerAnnotation>().diff(anns1, anns2);
80  	}
81  
82  	// XXX Unnecessary copy-pasting
83  	private static SetDiff<RowAnnotation, TransportRowAnnotation> diffRA(
84  			Set<RowAnnotation> anns1,
85  			Set<RowAnnotation> anns2) {
86  		if (anns2 == null && anns1 != null) {
87  			return null;
88  		}
89  		if (anns1==null) {
90  			anns1 = Collections.emptySet();
91  		}
92  		if (anns2==null) {
93  			anns2 = Collections.emptySet();
94  		}
95  		return new SetDiff.Differ<RowAnnotation,TransportRowAnnotation>().diff(anns1, anns2);
96  	}
97  
98  	
99  	public static ServerSideDocDiff fromTransportDiff(TransportDiff diff) {
100 		return new ServerSideDocDiff(
101 				(LinkedList<Patch>) getDmp().patch_fromText(diff.patchesAsString),
102 				MarkerSetDiff.fromTransportDiff(diff.markerSetDiff),
103 				rowAnnsFromTransport(diff.rowAnnDiff),
104 				markerAnnsFromTransport(diff.markerAnnDiff));
105 	}
106 	
107 	// XXX Unnecessary copy-pasting
108 	private static SetDiff<RowAnnotation,TransportRowAnnotation> rowAnnsFromTransport(
109 			TransportSetDiffForRowAnnotations rowAnnDiff) {
110 		return rowAnnDiff==null ? null : SetDiff.fromTransport(rowAnnDiff);
111 	}
112 	
113 	// XXX Unnecessary copy-pasting
114 	private static SetDiff<MarkerAnnotation,TransportMarkerAnnotation> markerAnnsFromTransport(
115 			TransportSetDiffForMarkerAnnotations markerAnnDiff) {
116 		return markerAnnDiff==null ? null :  SetDiff.fromTransport(markerAnnDiff);
117 	}
118 
119 	private ServerSideDocDiff(LinkedList<Patch> patches, MarkerSetDiff markerSetDiff,
120 			SetDiff<RowAnnotation,TransportRowAnnotation> rowAnnDiff,
121 			SetDiff<MarkerAnnotation,TransportMarkerAnnotation> markerAnnDiff) {
122 		this.patches = patches;
123 		this.markerSetDiff = markerSetDiff;
124 		this.rowAnnDiff = rowAnnDiff;
125 		this.markerAnnDiff = markerAnnDiff;
126 	}
127 
128 	public ServerSideDocDiff(LinkedList<Patch> patches) {
129 		this(patches, null, null, null);
130 	}
131 
132 	public String getPatchesString() {
133 		return getDmp().patch_toText(patches);
134 	}
135 	
136 	public List<Patch> getPatches() {
137 		return Collections.unmodifiableList(patches);
138 	}
139 
140 	
141 	public AceDoc applyTo(AceDoc doc) {
142 		String text = (String)getDmp().patch_apply(patches, doc.getText())[0];
143 		Map<String, AceMarker> markers = markerSetDiff==null ? doc.getMarkers() : markerSetDiff.applyTo(doc.getMarkers(), text);
144 		Set<RowAnnotation> rowAnns = rowAnnDiff==null ? null : rowAnnDiff.applyTo(doc.getRowAnnotations());
145 		Set<MarkerAnnotation> markerAnns = markerAnnDiff==null ? null : markerAnnDiff.applyTo(doc.getMarkerAnnotations());
146 		return new AceDoc(text, markers, rowAnns, markerAnns);
147 	}
148 	
149 	public String applyTo(String text) {
150 		return (String)getDmp().patch_apply(patches, text)[0];
151 	}
152 
153 	public TransportDiff asTransport() {
154 		TransportDiff d = new TransportDiff();
155 		d.patchesAsString = getPatchesString();
156 		d.markerSetDiff = markerSetDiff==null ? null : markerSetDiff.asTransportDiff();
157 		d.rowAnnDiff = rowAnnDiff==null ? null : rowAnnDiff.asTransportRowAnnotations();
158 		d.markerAnnDiff = markerAnnDiff==null ? null : markerAnnDiff.asTransportMarkerAnnotations();
159 		return d;
160 	}
161 
162 	public boolean isIdentity() {
163 		return patches.isEmpty() && (markerSetDiff==null || markerSetDiff.isIdentity()); // TODO?
164 	}
165 	
166 	@Override
167 	public String toString() {
168 		return "---ServerSideDocDiff---\n" + getPatchesString()+"\n"+markerSetDiff+"\nrad:"+rowAnnDiff+", mad:"+markerAnnDiff;
169 	}
170 
171 
172 	public static ServerSideDocDiff newMarkersAndAnnotations(
173 			MarkerSetDiff msd, SetDiff<MarkerAnnotation,TransportMarkerAnnotation> mad) {
174 		LinkedList<Patch> patches = new LinkedList<Patch>();
175 		SetDiff<RowAnnotation,TransportRowAnnotation> rowAnnDiff =
176 				new SetDiff<RowAnnotation, TransportRowAnnotation>();
177 		return new ServerSideDocDiff(patches, msd, rowAnnDiff, mad);
178 	}
179 	
180 }