1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.store.legacy.doc.internal

File ListAttachmentArchive.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart3.png
80% of files have more coverage

Code metrics

24
94
18
2
357
224
33
0.35
5.22
9
1.83

Classes

Class Line # Actions
ListAttachmentArchive 45 93 0% 32 94
0.2985074529.9%
ListAttachmentArchive.XWikiAttachmentVersionComparitor 343 1 0% 1 0
1.0100%
 

Contributing tests

This file is covered by 3 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.store.legacy.doc.internal;
21   
22    import java.io.ByteArrayInputStream;
23    import java.util.ArrayList;
24    import java.util.Collections;
25    import java.util.Comparator;
26    import java.util.Date;
27    import java.util.List;
28   
29    import org.suigeneris.jrcs.rcs.Archive;
30    import org.suigeneris.jrcs.rcs.Version;
31    import org.suigeneris.jrcs.rcs.impl.Node;
32    import org.suigeneris.jrcs.util.ToString;
33   
34    import com.xpn.xwiki.XWikiContext;
35    import com.xpn.xwiki.XWikiException;
36    import com.xpn.xwiki.doc.XWikiAttachment;
37    import com.xpn.xwiki.doc.XWikiAttachmentArchive;
38   
39    /**
40    * Implementation of an archive for XWikiAttachment based on a simple list of XWikiAttachments.
41    *
42    * @version $Id: 593d5e2e6d43f409a122ecba722b570e055c0191 $
43    * @since 3.0M2
44    */
 
45    public class ListAttachmentArchive extends XWikiAttachmentArchive
46    {
47    /**
48    * Generic message to put in any exception which occurs in this class.
49    */
50    private static final String GENERIC_EXCEPTION_MESSAGE =
51    "Exception while manipulating the archive for attachment {0}";
52   
53    /**
54    * Message to place in exceptions which are thrown because functions are not available in this implementation.
55    */
56    private static final String NOT_IMPLEMENTED_MESSAGE =
57    "This function is not available in this implementation.";
58   
59    /**
60    * The attachment which this is an archive of.
61    */
62    private XWikiAttachment attachment;
63   
64    /**
65    * A list of all the revisions of the attachment in this archive ordered by version number ascending.
66    */
67    private final List<XWikiAttachment> revisions = new ArrayList<XWikiAttachment>();
68   
69    /**
70    * The Constructor.
71    *
72    * @param attachment the attachment to associate with this archive.
73    */
 
74  0 toggle public ListAttachmentArchive(final XWikiAttachment attachment)
75    {
76  0 this.attachment = attachment;
77    }
78   
79    /**
80    * Constructor from List. Create a new instance of ListAttachmentArchive from a list of attachments.
81    *
82    * @param revisions a List of XWikiAttachment revisions to put in this archive. All revisions are the same
83    * attachment and thus must have the same ID.
84    */
 
85  5 toggle public ListAttachmentArchive(final List<XWikiAttachment> revisions)
86    {
87    // Empty list:
88  5 if (revisions.size() == 0) {
89  0 return;
90    }
91   
92  5 this.revisions.addAll(revisions);
93  5 Collections.sort(this.revisions, XWikiAttachmentVersionComparitor.INSTANCE);
94   
95    // Sanity check, all revisions should have the same ID.
96  5 long id = revisions.get(0).getId();
97  5 final String firstAttachName = revisions.get(0).getFilename();
98   
99  5 for (XWikiAttachment attach : revisions) {
100  15 if (attach.getId() != id) {
101  0 throw new IllegalArgumentException("Attachment " + attach.getFilename() + " has a "
102    + "different ID than the first attachment ( "
103    + firstAttachName + " ) so they cannot all be "
104    + "revisions of the same attachment.");
105    }
106  15 attach.setAttachment_archive(this);
107    }
108   
109    // Set the attachment for this archive to the latest version.
110  5 this.attachment = this.revisions.get(revisions.size() - 1);
111    }
112   
 
113  0 toggle @Override
114    public Object clone()
115    {
116  0 final ListAttachmentArchive out = new ListAttachmentArchive(cloneAttachment(this.attachment));
117  0 out.attachment.setAttachment_archive(out);
118  0 for (XWikiAttachment revision : this.revisions) {
119  0 final XWikiAttachment revClone = cloneAttachment(revision);
120  0 revClone.setAttachment_archive(out);
121  0 out.revisions.add(revClone);
122    }
123  0 return out;
124    }
125   
126    /**
127    * Clone an attachment but not it's archive.{@link ListAttachmentArchive#clone()} calls {@link
128    * XWikiAttachment#clone()} and if the attachment is associated with an archive it call clone the archive. This
129    * function prevents an infinite loop.
130    *
131    * @param original an attachment to clone.
132    * @return a clone of original which has no XWikiAttachmentArchive attached.
133    */
 
134  12 toggle private static XWikiAttachment cloneAttachment(final XWikiAttachment original)
135    {
136  12 final XWikiAttachmentArchive arch = original.getAttachment_archive();
137  12 try {
138  12 original.setAttachment_archive(null);
139  12 return (XWikiAttachment) original.clone();
140    } finally {
141  12 original.setAttachment_archive(arch);
142    }
143    }
144   
 
145  0 toggle @Override
146    @Deprecated
147    public Archive getRCSArchive()
148    {
149  0 throw new RuntimeException(NOT_IMPLEMENTED_MESSAGE);
150    }
151   
 
152  0 toggle @Override
153    @Deprecated
154    public void setRCSArchive(final Archive rcsArchive)
155    {
156  0 throw new RuntimeException(NOT_IMPLEMENTED_MESSAGE);
157    }
158   
159    /**
160    * Convert this attachment archive into JRCS format.
161    *
162    * @param context the XWikiContext for the request.
163    * @return this archive in JRCS format.
164    * @throws Exception if something goes wrong while serializing the attachment to XML or inserting it into the RCS
165    * archive.
166    */
 
167  0 toggle private Archive toRCS(final XWikiContext context) throws Exception
168    {
169  0 final Version[] versions = this.getVersions();
170  0 Archive rcsArch = null;
171  0 for (XWikiAttachment rev : this.revisions) {
172  0 final String sdata = rev.toStringXML(true, false, context);
173  0 final Object[] lines = ToString.stringToArray(sdata);
174  0 if (rcsArch == null) {
175    // First cycle.
176  0 rcsArch = new Archive(lines, rev.getFilename(), rev.getVersion());
177    } else {
178  0 rcsArch.addRevision(lines, "");
179    }
180    }
181  0 return rcsArch;
182    }
183   
184    /**
185    * @param rcsArchive the RCS archive to import.
186    * @throws Exception if getting a revision from the RCS archive or deserializing an attachment from XML fails
187    */
 
188  0 toggle private void fromRCS(final Archive rcsArchive) throws Exception
189    {
190  0 if (rcsArchive == null) {
191  0 return;
192    }
193   
194  0 final Node[] nodes = rcsArchive.changeLog();
195  0 for (int i = nodes.length - 1; i > -1; i--) {
196  0 final Object[] lines = rcsArchive.getRevision(nodes[i].getVersion());
197  0 final StringBuilder content = new StringBuilder();
198  0 for (int j = 0; j < lines.length; j++) {
199  0 String line = lines[j].toString();
200  0 content.append(line);
201  0 if (j != lines.length - 1) {
202  0 content.append("\n");
203    }
204    }
205  0 final XWikiAttachment rev = new XWikiAttachment();
206  0 rev.fromXML(content.toString());
207  0 rev.setDoc(this.getAttachment().getDoc());
208  0 rev.setAttachment_archive(this);
209   
210    // this should not be necessary, keeping to maintain behavior.
211  0 rev.setVersion(nodes[i].getVersion().toString());
212   
213  0 revisions.add(rev);
214    }
215    }
216   
217    /**
218    * {@inheritDoc}
219    * <p>
220    * Not implemented, always returns an empty array.
221    * </p>
222    *
223    * @see com.xpn.xwiki.doc.XWikiAttachmentArchive#getArchive()
224    */
 
225  0 toggle @Override
226    public byte[] getArchive()
227    {
228  0 return new byte[0];
229    }
230   
 
231  0 toggle @Override
232    public byte[] getArchive(final XWikiContext context) throws XWikiException
233    {
234  0 try {
235  0 return this.toRCS(context).toByteArray();
236    } catch (Exception e) {
237  0 if (e instanceof XWikiException) {
238  0 throw (XWikiException) e;
239    }
240  0 Object[] args = { getAttachment().getFilename() };
241  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
242    XWikiException.ERROR_XWIKI_STORE_ATTACHMENT_ARCHIVEFORMAT,
243    GENERIC_EXCEPTION_MESSAGE, e, args);
244    }
245    }
246   
 
247  0 toggle @Override
248    public void setArchive(final byte[] data) throws XWikiException
249    {
250  0 this.revisions.clear();
251   
252  0 if ((data != null) && (data.length == 0)) {
253  0 try {
254  0 final ByteArrayInputStream is = new ByteArrayInputStream(data);
255  0 final Archive rcsArchive = new Archive(getAttachment().getFilename(), is);
256  0 this.setRCSArchive(rcsArchive);
257    } catch (Exception e) {
258  0 if (e instanceof XWikiException) {
259  0 throw (XWikiException) e;
260    }
261  0 Object[] args = { getAttachment().getFilename() };
262  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
263    XWikiException.ERROR_XWIKI_STORE_ATTACHMENT_ARCHIVEFORMAT,
264    GENERIC_EXCEPTION_MESSAGE, e, args);
265    }
266    }
267    }
268   
 
269  0 toggle @Override
270    public void updateArchive(final XWikiContext context)
271    {
272  0 this.update();
273    }
274   
275    /**
276    * Update the archive, increment the attachment version, set the date on the attachment and add the attachment to
277    * the list.
278    */
 
279  0 toggle private void update()
280    {
281  0 final XWikiAttachment attach = this.getAttachment();
282  0 attach.incrementVersion();
283  0 attach.setDate(new Date());
284    // Clone the attachment but don't clone this archive.
285  0 final XWikiAttachment clone = cloneAttachment(attach);
286  0 clone.setAttachment_archive(this);
287  0 this.revisions.add(clone);
288    }
289   
 
290  26 toggle @Override
291    public XWikiAttachment getAttachment()
292    {
293  26 return this.attachment;
294    }
295   
 
296  2 toggle @Override
297    public void setAttachment(final XWikiAttachment attachment)
298    {
299  2 this.attachment = attachment;
300    }
301   
 
302  8 toggle @Override
303    public Version[] getVersions()
304    {
305  8 final Version[] versions = new Version[this.revisions.size()];
306  8 int i = this.revisions.size();
307  8 for (XWikiAttachment attach : this.revisions) {
308  24 i--;
309  24 versions[i] = attach.getRCSVersion();
310    }
311   
312  8 return versions;
313    }
314   
 
315  12 toggle @Override
316    public XWikiAttachment getRevision(final XWikiAttachment attachment,
317    final String rev,
318    final XWikiContext context)
319    {
320  12 if (rev == null) {
321  0 return null;
322    }
323   
324  12 for (XWikiAttachment attach : this.revisions) {
325  24 if (rev.equals(attach.getVersion())) {
326  12 final XWikiAttachment out = cloneAttachment(attach);
327  12 out.setAttachment_archive(this);
328   
329    // This is silly, we set the attachment document and passed value.
330    // Keeping to maintain current behavior.
331  12 out.setDoc(attachment.getDoc());
332   
333  12 return out;
334    }
335    }
336   
337  0 return null;
338    }
339   
340    /**
341    * A comparitor which compares attachments by version number.
342    */
 
343    private static class XWikiAttachmentVersionComparitor implements Comparator<XWikiAttachment>
344    {
345    /**
346    * A single instance to use instead of constructing one each time.
347    */
348    public static final XWikiAttachmentVersionComparitor INSTANCE =
349    new XWikiAttachmentVersionComparitor();
350   
 
351  10 toggle @Override
352    public int compare(final XWikiAttachment a, final XWikiAttachment b)
353    {
354  10 return a.getRCSVersion().compareTo(b.getRCSVersion());
355    }
356    }
357    }