KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > docdiff > DiffGenerator


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.outerj.daisy.docdiff;
17
18 import org.outerj.daisy.repository.*;
19 import org.outerj.daisy.xmlutil.XmlEncodingDetector;
20
21 import java.util.Arrays JavaDoc;
22
23 /**
24  * This class generates information about the differences between two versions of a document.
25  * The result of the comparision is generated as a series of events to an instance of
26  * {@link DocDiffOutput}.
27  */

28 public class DiffGenerator {
29     private Version version1;
30     private Version version2;
31     private boolean hasRun = false;
32     private DocDiffOutput output;
33     private static final long DIFF_LIMIT = 500000; // about half a meg
34

35     public DiffGenerator(Version version1, Version version2, DocDiffOutput output) {
36         this.version1 = version1;
37         this.version2 = version2;
38         this.output = output;
39     }
40
41     public void generateDiff() throws Exception JavaDoc {
42         if (hasRun)
43             throw new Exception JavaDoc("DiffGenerator object can only be used once");
44         hasRun = true;
45
46         output.begin();
47
48         generatePartDifferences();
49         generateFieldDifferences();
50         generateLinkDifferences();
51
52         output.end();
53
54         version1 = null;
55         version2 = null;
56         output = null;
57     }
58
59     private void generatePartDifferences() throws Exception JavaDoc {
60         output.beginPartChanges();
61
62         Part[] version1Parts = version1.getPartsInOrder().getArray();
63         Part[] version2Parts = version2.getPartsInOrder().getArray();
64
65         // Search parts of version1 that are removed in version2
66
for (int i = 0; i < version1Parts.length; i++) {
67             long typeId = version1Parts[i].getTypeId();
68             if (findPart(version2Parts, typeId) == null) {
69                 output.partRemoved(version1Parts[i]);
70             }
71         }
72
73         // Search for new or updated parts
74
for (int i = 0; i < version2Parts.length; i++) {
75             Part version2Part = version2Parts[i];
76             Part version1Part = findPart(version1Parts, version2Part.getTypeId());
77             if (version1Part == null) {
78                 output.partAdded(version2Part);
79             } else {
80                 if (version1Part.getSize() > DIFF_LIMIT || version2Part.getSize() > DIFF_LIMIT) {
81                     output.partMightBeUpdated(version2Part);
82                 } else {
83                     String JavaDoc version1MimeType = version1Part.getMimeType();
84                     String JavaDoc version2MimeType = version2Part.getMimeType();
85
86                     String JavaDoc version1FileName = version1Part.getFileName();
87                     String JavaDoc version2FileName = version2Part.getFileName();
88
89                     boolean mimeTypesEqual = version1MimeType.equals(version2MimeType);
90                     boolean fileNamesEqual = (version1FileName == null && version2FileName == null) || (version1FileName != null && version1FileName.equals(version2FileName));
91                     boolean dataEqual;
92
93                     byte[] version1Data = null;
94                     byte[] version2Data = null;
95
96                     if (version1Part.getSize() != version2Part.getSize()) {
97                         dataEqual = false;
98                     } else {
99                         version1Data = version1Part.getData();
100                         version2Data = version2Part.getData();
101                         dataEqual = Arrays.equals(version1Data, version2Data);
102                     }
103
104                     if (mimeTypesEqual && fileNamesEqual && dataEqual) {
105                         // report there are no changes
106
output.partUnchanged(version2Part);
107                     } else {
108                         String JavaDoc part1String = null, part2String = null;
109                         if (!dataEqual && version1MimeType.startsWith("text/") && version2MimeType.startsWith("text/")) {
110                             // load data if it not already happened
111
if (version1Data == null) {
112                                 version1Data = version1Part.getData();
113                                 version2Data = version2Part.getData();
114                             }
115
116                             // TODO do some effort to detect the encoding of non-xml text formats, for example using
117
// the library found at http://jchardet.sourceforge.net/
118
if (version1MimeType.equals("text/xml"))
119                                 part1String = new String JavaDoc(version1Data, XmlEncodingDetector.detectEncoding(version1Data));
120                             else
121                                 part1String = new String JavaDoc(version1Data);
122
123                             if (version1MimeType.equals("text/xml"))
124                                 part2String = new String JavaDoc(version2Data, XmlEncodingDetector.detectEncoding(version2Data));
125                             else
126                                 part2String = new String JavaDoc(version2Data);
127                         }
128
129                         output.partUpdated(version1Part, version2Part, part1String, part2String);
130
131                         version1Data = null;
132                         version2Data = null;
133                     }
134                 }
135             }
136         }
137
138         output.endPartChanges();
139     }
140
141     private void generateFieldDifferences() throws Exception JavaDoc {
142         output.beginFieldChanges();
143
144         Field[] version1Fields = version1.getFields().getArray();
145         Field[] version2Fields = version2.getFields().getArray();
146
147         // Search fields of version1 that are removed in version2
148
for (int i = 0; i < version1Fields.length; i++) {
149             long typeId = version1Fields[i].getTypeId();
150             if (findField(version2Fields, typeId) == null) {
151                 output.fieldRemoved(version1Fields[i]);
152             }
153         }
154
155         // Search for new or updated fields
156
for (int i = 0; i < version2Fields.length; i++) {
157             Field version2Field = version2Fields[i];
158             Field version1Field = findField(version1Fields, version2Field.getTypeId());
159             if (version1Field == null) {
160                 output.fieldAdded(version2Field);
161             } else {
162                 boolean updated = (version1Field.isMultiValue() && !Arrays.equals((Object JavaDoc[])version1Field.getValue(), (Object JavaDoc[])version2Field.getValue()))
163                     || (!version1Field.isMultiValue() && !version1Field.getValue().equals(version2Field.getValue()));
164                 if (updated) {
165                     output.fieldUpdated(version1Field, version2Field);
166                 } else {
167                     // unchanged: don't report
168
}
169             }
170         }
171
172         output.endFieldChanges();
173     }
174
175     private void generateLinkDifferences() throws Exception JavaDoc {
176         output.beginLinkChanges();
177
178         Link[] version1Links = version1.getLinks().getArray();
179         Link[] version2Links = version2.getLinks().getArray();
180
181         // Search links of version1 that are removed in version2
182
for (int i = 0; i < version1Links.length; i++) {
183             if (findLink(version2Links, version1Links[i]) == null) {
184                 output.linkRemoved(version1Links[i]);
185             }
186         }
187
188         // Search for new or updated links
189
for (int i = 0; i < version2Links.length; i++) {
190             Link version2Link = version2Links[i];
191             Link version1Link = findLink(version1Links, version2Link);
192             if (version1Link == null) {
193                 output.linkAdded(version2Link);
194             } else {
195                 // unchanged link: don't report
196
}
197         }
198
199         output.endLinkChanges();
200     }
201
202     private Part findPart(Part[] parts, long typeId) {
203         for (int k = 0; k < parts.length; k++) {
204             if (parts[k].getTypeId() == typeId) {
205                 return parts[k];
206             }
207         }
208         return null;
209     }
210
211     private Field findField(Field[] fields, long typeId) {
212         for (int k = 0; k < fields.length; k++) {
213             if (fields[k].getTypeId() == typeId) {
214                 return fields[k];
215             }
216         }
217         return null;
218     }
219
220     private Link findLink(Link[] links, Link link) {
221         for (int k = 0; k < links.length; k++) {
222             if (links[k].getTitle().equals(link.getTitle()) && links[k].getTarget().equals(link.getTarget())) {
223                 return links[k];
224             }
225         }
226         return null;
227     }
228
229 }
230
Popular Tags