1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.doc

File XWikiAttachmentArchive.java

 

Coverage histogram

../../../../img/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

32
82
17
1
358
194
42
0.51
4.82
17
2.47

Classes

Class Line # Actions
XWikiAttachmentArchive 44 82 0% 42 33
0.748091674.8%
 

Contributing tests

This file is covered by 8 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 com.xpn.xwiki.doc;
21   
22    import java.io.ByteArrayInputStream;
23    import java.io.Reader;
24    import java.io.StringReader;
25    import java.util.Date;
26   
27    import org.apache.commons.lang3.ArrayUtils;
28    import org.apache.commons.lang3.StringUtils;
29    import org.slf4j.Logger;
30    import org.slf4j.LoggerFactory;
31    import org.suigeneris.jrcs.rcs.Archive;
32    import org.suigeneris.jrcs.rcs.Version;
33    import org.suigeneris.jrcs.rcs.impl.Node;
34    import org.suigeneris.jrcs.util.ToString;
35   
36    import com.xpn.xwiki.XWikiContext;
37    import com.xpn.xwiki.XWikiException;
38   
39    /**
40    * JRCS based implementation of an archive for XWikiAttachment.
41    *
42    * @version $Id: 8cd7c5243ad63ee00398f62b4955af8a858f209f $
43    */
 
44    public class XWikiAttachmentArchive implements Cloneable
45    {
46    /** Generic message to put in any exception which occurs in this class. */
47    private static final String GENERIC_EXCEPTION_MESSAGE =
48    "Exception while manipulating the archive for attachment {0}";
49   
50    /** The log, used to log if there is an error while cloning the archive. */
51    private static final Logger LOGGER = LoggerFactory.getLogger(XWikiAttachmentArchive.class);
52   
53    /** The attachment which this is an archive of. */
54    private XWikiAttachment attachment;
55   
56    /** The underlying JRCS archive. */
57    private Archive archive;
58   
59    /**
60    * @return the id of the attachment which this archive is associated with.
61    */
 
62  2192 toggle public long getId()
63    {
64  2192 return this.attachment.getId();
65    }
66   
67    /**
68    * This does nothing and is only here to satisfy Hibernate.
69    *
70    * @param id the id of the attachment which this archive is associated with, unused.
71    */
 
72  871 toggle public void setId(final long id)
73    {
74    // Do nothing as this is here only to please hibernate.
75    }
76   
 
77  442 toggle @Override
78    public Object clone()
79    {
80  442 XWikiAttachmentArchive attachmentarchive = null;
81  442 try {
82  442 attachmentarchive = getClass().newInstance();
83    } catch (Exception e) {
84    // This should not happen
85  0 LOGGER.error("Error while attachmentArchive.clone()", e);
86    }
87   
88  442 attachmentarchive.setAttachment(getAttachment());
89  442 attachmentarchive.setRCSArchive(getRCSArchive());
90   
91  442 return attachmentarchive;
92    }
93   
94    /**
95    * @deprecated since 2.6M1 please do not use this, it is bound to a jrcs based implementation.
96    * @return a JRCS archive.
97    */
 
98  461 toggle @Deprecated
99    public Archive getRCSArchive()
100    {
101  461 return this.archive;
102    }
103   
104    /**
105    * @deprecated since 2.6M1 please do not use this, it is bound to a jrcs based implementation.
106    * @param archive a JRCS archive.
107    */
 
108  443 toggle @Deprecated
109    public void setRCSArchive(final Archive archive)
110    {
111  443 this.archive = archive;
112    }
113   
114    /**
115    * Get the archive if it is currently stored in RAM.
116    *
117    * @return a String representation of a JRCS archive.
118    * @throws XWikiException if anything goes wrong.
119    * @since 7.1M1
120    */
 
121  22 toggle public String getArchiveAsString() throws XWikiException
122    {
123  22 if (this.archive == null) {
124  0 return "";
125    } else {
126  22 return this.archive.toString(Archive.RCS_NEWLINE);
127    }
128    }
129   
130    /**
131    * Get the archive, loading it from the database if necessary.
132    *
133    * @param context the XWikiContext for the request used to load the correct attachment archive from the database.
134    * @return a String representation of a JRCS archive.
135    * @throws XWikiException if anything goes wrong.
136    * @since 7.1M1
137    */
 
138  0 toggle public String getArchiveAsString(final XWikiContext context) throws XWikiException
139    {
140  0 if (this.archive == null) {
141  0 if (context != null) {
142  0 updateArchive(context);
143    }
144    }
145   
146  0 return getArchiveAsString();
147    }
148   
149    /**
150    * Get the archive if it is currently stored in RAM.
151    *
152    * @return a byte array representation of a JRCS archive or an empty array if the archive is not available on the
153    * heap.
154    * @throws XWikiException if anything goes wrong.
155    */
 
156  883 toggle public byte[] getArchive() throws XWikiException
157    {
158  883 if (this.archive == null) {
159  774 return new byte[0];
160    } else {
161  109 return this.archive.toByteArray();
162    }
163    }
164   
165    /**
166    * Get the archive, loading it from the database if necessary.
167    *
168    * @param context the XWikiContext for the request used to load the correct attachment archive from the database.
169    * @return a byte array representation of a JRCS archive.
170    * @throws XWikiException if anything goes wrong.
171    */
 
172  0 toggle public byte[] getArchive(final XWikiContext context) throws XWikiException
173    {
174  0 if (this.archive == null) {
175  0 if (context != null) {
176  0 updateArchive(context);
177    }
178    }
179   
180  0 return getArchive();
181    }
182   
183    /**
184    * Set the archive from a byte array representation of a JRCS archive.
185    *
186    * @param data a byte array representation of a JRCS archive.
187    * @throws XWikiException if anything goes wrong.
188    */
 
189  40 toggle public void setArchive(final byte[] data) throws XWikiException
190    {
191  40 if (ArrayUtils.isEmpty(data)) {
192  0 this.archive = null;
193    } else {
194  40 try (ByteArrayInputStream is = new ByteArrayInputStream(data)) {
195  40 this.archive = new Archive(getAttachment().getFilename(), is);
196    } catch (Exception e) {
197  0 Object[] args = {getAttachment().getFilename()};
198  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
199    XWikiException.ERROR_XWIKI_STORE_ATTACHMENT_ARCHIVEFORMAT, GENERIC_EXCEPTION_MESSAGE, e, args);
200    }
201    }
202    }
203   
204    /**
205    * Set the archive from a byte array representation of a JRCS archive.
206    *
207    * @param data a String representation of a JRCS archive.
208    * @throws XWikiException if anything goes wrong.
209    * @since 7.1M1
210    */
 
211  15 toggle public void setArchive(final String data) throws XWikiException
212    {
213  15 if (StringUtils.isEmpty(data)) {
214  14 this.archive = null;
215    } else {
216  1 try (Reader reader = new StringReader(data)) {
217  1 this.archive = new Archive(getAttachment().getFilename(), reader);
218    } catch (Exception e) {
219  0 Object[] args = {getAttachment().getFilename()};
220  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
221    XWikiException.ERROR_XWIKI_STORE_ATTACHMENT_ARCHIVEFORMAT, GENERIC_EXCEPTION_MESSAGE, e, args);
222    }
223    }
224    }
225   
226    /**
227    * Update the archive.
228    *
229    * @param data not used for anything, the data is loaded from the attachment included with this archive.
230    * @param context the XWikiContext for the request used to load the correct attachment content from the database.
231    * @throws XWikiException if anything goes wrong.
232    * @deprecated since 7.1M1, use {@link #updateArchive(XWikiContext)} instead
233    */
 
234  0 toggle @Deprecated
235    public void updateArchive(final byte[] data, final XWikiContext context) throws XWikiException
236    {
237  0 updateArchive(context);
238    }
239   
240    /**
241    * Update the archive.
242    *
243    * @param context the XWikiContext for the request used to load the correct attachment content from the database.
244    * @throws XWikiException if anything goes wrong.
245    * @since 7.1M1
246    */
 
247  57 toggle public void updateArchive(final XWikiContext context) throws XWikiException
248    {
249  57 try {
250  57 this.attachment.incrementVersion();
251  57 this.attachment.setDate(new Date());
252  57 final Object[] lines = ToString.stringToArray(this.attachment.toStringXML(true, false, context));
253   
254  57 if (this.archive != null) {
255  7 this.archive.addRevision(lines, "");
256    } else {
257  50 this.archive = new Archive(lines, getAttachment().getFilename(), getAttachment().getVersion());
258    }
259   
260    // Set a standard author, since by default the operating system user is set, and it might contain confusing
261    // characters (JRCS is very fragile and breaks easily if a wrong value is used)
262  57 this.archive.findNode(this.archive.getRevisionVersion()).setAuthor("xwiki");
263    } catch (Exception e) {
264  0 Object[] args = {getAttachment().getFilename()};
265  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
266    XWikiException.ERROR_XWIKI_STORE_ATTACHMENT_ARCHIVEFORMAT, GENERIC_EXCEPTION_MESSAGE, e, args);
267    }
268    }
269   
270    /**
271    * @return the attachment which this is an archive for.
272    */
 
273  592 toggle public XWikiAttachment getAttachment()
274    {
275  592 return this.attachment;
276    }
277   
278    /**
279    * Set the attachment to associate with this archive. This is a dangerous function because it will not change the
280    * archive. Using this may cause an attachment to be associated with the wrong history.
281    *
282    * @param attachment the attachment to set for this archive.
283    */
 
284  1383 toggle public void setAttachment(final XWikiAttachment attachment)
285    {
286  1383 this.attachment = attachment;
287    }
288   
289    /**
290    * @return an array of versions which are available for this attachment, ordered by version number descending.
291    */
 
292  3 toggle public Version[] getVersions()
293    {
294  3 final Archive rcsArchive = getRCSArchive();
295   
296  3 Version[] versions;
297  3 if (rcsArchive != null) {
298  2 final Node[] nodes = rcsArchive.changeLog();
299  2 versions = new Version[nodes.length];
300  8 for (int i = 0; i < nodes.length; i++) {
301  6 versions[i] = nodes[i].getVersion();
302    }
303    } else {
304    // No archive means there is no history and only the current version
305  1 versions = new Version[] {this.attachment.getRCSVersion()};
306    }
307   
308  3 return versions;
309    }
310   
311    /**
312    * Get an old revision of the attachment which this is an archive of.
313    *
314    * @param attachment This attachment will be used to get the document to associate the attachment revision with.
315    * @param rev a String representation of the version to load.
316    * @param context the context for the request which needed this revision.
317    * @return an XWikiAttachment for the given revision.
318    * @throws XWikiException if any Exception is thrown while getting the revision.
319    */
 
320  16 toggle public XWikiAttachment getRevision(final XWikiAttachment attachment, final String rev, final XWikiContext context)
321    throws XWikiException
322    {
323  16 try {
324  16 final Archive rcsArchive = getRCSArchive();
325   
326  16 if (rcsArchive == null) {
327    // No archive means there is no history and only the current version.
328  2 return this.attachment.getVersion().equals(rev) ? this.attachment : null;
329    }
330   
331  14 final Version version = rcsArchive.getRevisionVersion(rev);
332  14 if (version == null) {
333    // The requested revision doesn't exist.
334  1 return null;
335    }
336  13 final Object[] lines = rcsArchive.getRevision(version);
337  13 final StringBuilder content = new StringBuilder();
338  239 for (int i = 0; i < lines.length; i++) {
339  226 String line = lines[i].toString();
340  226 content.append(line);
341  226 if (i != lines.length - 1) {
342  213 content.append("\n");
343    }
344    }
345   
346  13 final String scontent = content.toString();
347  13 final XWikiAttachment revattach = new XWikiAttachment();
348  13 revattach.fromXML(scontent);
349  13 revattach.setDoc(attachment.getDoc());
350  13 revattach.setVersion(rev);
351  13 return revattach;
352    } catch (Exception e) {
353  0 final Object[] args = {attachment.getFilename()};
354  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
355    XWikiException.ERROR_XWIKI_STORE_ATTACHMENT_ARCHIVEFORMAT, GENERIC_EXCEPTION_MESSAGE, e, args);
356    }
357    }
358    }