1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package com.xpn.xwiki.doc

File XWikiAttachmentArchive.java

 

Coverage histogram

../../../../img/srcFileCovDistChart6.png
72% of files have more coverage

Code metrics

32
79
17
1
354
191
42
0.53
4.65
17
2.47

Classes

Class Line # Actions
XWikiAttachmentArchive 44 79 0% 42 60
0.5312553.1%
 

Contributing tests

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