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

File XWikiDocument.java

 
verifySubHeadingVelocityVariableCorrectlyEvaluatedWhenUsedInSection: Failed to lookup default document displayer.
webRssDisplay: Failed to lookup default document displayer.
webRssFiltersHiddenDocuments: Failed to lookup default document displayer.
 

Coverage histogram

../../../../img/srcFileCovDistChart7.png
64% of files have more coverage

Code metrics

1,012
2,557
506
2
8,820
5,436
1,150
0.45
5.05
253
2.27

Classes

Class Line # Actions
XWikiDocument 193 2,548 0% 1,143 1,315
0.6759487467.6%
XWikiDocument.XWikiAttachmentToRemove 203 9 0% 7 10
0.411764741.2%
 

Contributing tests

This file is covered by 395 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.IOException;
24    import java.io.InputStream;
25    import java.io.OutputStream;
26    import java.io.StringWriter;
27    import java.lang.ref.SoftReference;
28    import java.lang.reflect.Constructor;
29    import java.lang.reflect.Method;
30    import java.lang.reflect.Type;
31    import java.net.URL;
32    import java.security.MessageDigest;
33    import java.security.NoSuchAlgorithmException;
34    import java.util.ArrayList;
35    import java.util.Collection;
36    import java.util.Collections;
37    import java.util.Date;
38    import java.util.HashMap;
39    import java.util.HashSet;
40    import java.util.Iterator;
41    import java.util.LinkedHashSet;
42    import java.util.List;
43    import java.util.Locale;
44    import java.util.Map;
45    import java.util.Map.Entry;
46    import java.util.Objects;
47    import java.util.Set;
48    import java.util.SortedMap;
49    import java.util.TreeMap;
50    import java.util.Vector;
51    import java.util.regex.Matcher;
52    import java.util.regex.Pattern;
53    import java.util.zip.ZipEntry;
54    import java.util.zip.ZipOutputStream;
55   
56    import javax.inject.Provider;
57    import javax.servlet.http.HttpServletResponse;
58   
59    import org.apache.commons.lang3.BooleanUtils;
60    import org.apache.commons.lang3.ObjectUtils;
61    import org.apache.commons.lang3.StringUtils;
62    import org.apache.commons.lang3.exception.ExceptionUtils;
63    import org.apache.velocity.VelocityContext;
64    import org.dom4j.Document;
65    import org.dom4j.Element;
66    import org.dom4j.dom.DOMDocument;
67    import org.dom4j.io.DocumentResult;
68    import org.dom4j.io.OutputFormat;
69    import org.slf4j.Logger;
70    import org.slf4j.LoggerFactory;
71    import org.suigeneris.jrcs.diff.Diff;
72    import org.suigeneris.jrcs.diff.DifferentiationFailedException;
73    import org.suigeneris.jrcs.diff.Revision;
74    import org.suigeneris.jrcs.diff.delta.Delta;
75    import org.suigeneris.jrcs.rcs.Version;
76    import org.suigeneris.jrcs.util.ToString;
77    import org.xwiki.bridge.DocumentModelBridge;
78    import org.xwiki.component.util.DefaultParameterizedType;
79    import org.xwiki.context.Execution;
80    import org.xwiki.context.ExecutionContextException;
81    import org.xwiki.context.ExecutionContextManager;
82    import org.xwiki.display.internal.DocumentDisplayer;
83    import org.xwiki.display.internal.DocumentDisplayerParameters;
84    import org.xwiki.filter.input.DefaultInputStreamInputSource;
85    import org.xwiki.filter.input.InputSource;
86    import org.xwiki.filter.input.StringInputSource;
87    import org.xwiki.filter.instance.input.DocumentInstanceInputProperties;
88    import org.xwiki.filter.instance.output.DocumentInstanceOutputProperties;
89    import org.xwiki.filter.output.DefaultOutputStreamOutputTarget;
90    import org.xwiki.filter.output.DefaultWriterOutputTarget;
91    import org.xwiki.filter.output.OutputTarget;
92    import org.xwiki.filter.xar.input.XARInputProperties;
93    import org.xwiki.filter.xar.output.XAROutputProperties;
94    import org.xwiki.filter.xml.output.DefaultResultOutputTarget;
95    import org.xwiki.job.event.status.JobProgressManager;
96    import org.xwiki.localization.ContextualLocalizationManager;
97    import org.xwiki.localization.LocaleUtils;
98    import org.xwiki.model.EntityType;
99    import org.xwiki.model.internal.reference.DefaultSymbolScheme;
100    import org.xwiki.model.internal.reference.LocalStringEntityReferenceSerializer;
101    import org.xwiki.model.internal.reference.LocalUidStringEntityReferenceSerializer;
102    import org.xwiki.model.reference.AttachmentReference;
103    import org.xwiki.model.reference.DocumentReference;
104    import org.xwiki.model.reference.DocumentReferenceResolver;
105    import org.xwiki.model.reference.EntityReference;
106    import org.xwiki.model.reference.EntityReferenceProvider;
107    import org.xwiki.model.reference.EntityReferenceResolver;
108    import org.xwiki.model.reference.EntityReferenceSerializer;
109    import org.xwiki.model.reference.LocalDocumentReference;
110    import org.xwiki.model.reference.ObjectPropertyReference;
111    import org.xwiki.model.reference.ObjectReference;
112    import org.xwiki.model.reference.ObjectReferenceResolver;
113    import org.xwiki.model.reference.SpaceReference;
114    import org.xwiki.model.reference.WikiReference;
115    import org.xwiki.query.Query;
116    import org.xwiki.query.QueryException;
117    import org.xwiki.query.QueryFilter;
118    import org.xwiki.rendering.block.Block;
119    import org.xwiki.rendering.block.Block.Axes;
120    import org.xwiki.rendering.block.HeaderBlock;
121    import org.xwiki.rendering.block.LinkBlock;
122    import org.xwiki.rendering.block.MacroBlock;
123    import org.xwiki.rendering.block.SectionBlock;
124    import org.xwiki.rendering.block.XDOM;
125    import org.xwiki.rendering.block.match.ClassBlockMatcher;
126    import org.xwiki.rendering.block.match.MacroBlockMatcher;
127    import org.xwiki.rendering.listener.reference.ResourceReference;
128    import org.xwiki.rendering.listener.reference.ResourceType;
129    import org.xwiki.rendering.parser.ContentParser;
130    import org.xwiki.rendering.parser.MissingParserException;
131    import org.xwiki.rendering.parser.ParseException;
132    import org.xwiki.rendering.parser.Parser;
133    import org.xwiki.rendering.renderer.BlockRenderer;
134    import org.xwiki.rendering.renderer.printer.DefaultWikiPrinter;
135    import org.xwiki.rendering.renderer.printer.WikiPrinter;
136    import org.xwiki.rendering.syntax.Syntax;
137    import org.xwiki.rendering.syntax.SyntaxFactory;
138    import org.xwiki.rendering.transformation.RenderingContext;
139    import org.xwiki.rendering.transformation.TransformationContext;
140    import org.xwiki.rendering.transformation.TransformationException;
141    import org.xwiki.rendering.transformation.TransformationManager;
142    import org.xwiki.rendering.util.ErrorBlockGenerator;
143    import org.xwiki.security.authorization.ContextualAuthorizationManager;
144    import org.xwiki.security.authorization.Right;
145    import org.xwiki.velocity.VelocityManager;
146    import org.xwiki.xar.internal.model.XarDocumentModel;
147    import org.xwiki.xml.XMLUtils;
148   
149    import com.xpn.xwiki.CoreConfiguration;
150    import com.xpn.xwiki.XWiki;
151    import com.xpn.xwiki.XWikiConstant;
152    import com.xpn.xwiki.XWikiContext;
153    import com.xpn.xwiki.XWikiException;
154    import com.xpn.xwiki.api.DocumentSection;
155    import com.xpn.xwiki.criteria.impl.RevisionCriteria;
156    import com.xpn.xwiki.doc.merge.MergeConfiguration;
157    import com.xpn.xwiki.doc.merge.MergeResult;
158    import com.xpn.xwiki.doc.rcs.XWikiRCSNodeInfo;
159    import com.xpn.xwiki.internal.AbstractNotifyOnUpdateList;
160    import com.xpn.xwiki.internal.cache.rendering.RenderingCache;
161    import com.xpn.xwiki.internal.filter.XWikiDocumentFilterUtils;
162    import com.xpn.xwiki.internal.merge.MergeUtils;
163    import com.xpn.xwiki.internal.render.LinkedResourceHelper;
164    import com.xpn.xwiki.internal.render.OldRendering;
165    import com.xpn.xwiki.internal.xml.DOMXMLWriter;
166    import com.xpn.xwiki.internal.xml.XMLWriter;
167    import com.xpn.xwiki.objects.BaseCollection;
168    import com.xpn.xwiki.objects.BaseObject;
169    import com.xpn.xwiki.objects.BaseObjectReference;
170    import com.xpn.xwiki.objects.BaseProperty;
171    import com.xpn.xwiki.objects.LargeStringProperty;
172    import com.xpn.xwiki.objects.ListProperty;
173    import com.xpn.xwiki.objects.ObjectDiff;
174    import com.xpn.xwiki.objects.PropertyInterface;
175    import com.xpn.xwiki.objects.classes.BaseClass;
176    import com.xpn.xwiki.objects.classes.ListClass;
177    import com.xpn.xwiki.objects.classes.PropertyClass;
178    import com.xpn.xwiki.objects.classes.StaticListClass;
179    import com.xpn.xwiki.objects.classes.TextAreaClass;
180    import com.xpn.xwiki.store.XWikiAttachmentStoreInterface;
181    import com.xpn.xwiki.store.XWikiStoreInterface;
182    import com.xpn.xwiki.store.XWikiVersioningStoreInterface;
183    import com.xpn.xwiki.user.api.XWikiRightService;
184    import com.xpn.xwiki.util.Util;
185    import com.xpn.xwiki.validation.XWikiValidationInterface;
186    import com.xpn.xwiki.validation.XWikiValidationStatus;
187    import com.xpn.xwiki.web.EditForm;
188    import com.xpn.xwiki.web.ObjectAddForm;
189    import com.xpn.xwiki.web.ObjectPolicyType;
190    import com.xpn.xwiki.web.Utils;
191    import com.xpn.xwiki.web.XWikiRequest;
192   
 
193    public class XWikiDocument implements DocumentModelBridge, Cloneable
194    {
195    private static final Logger LOGGER = LoggerFactory.getLogger(XWikiDocument.class);
196   
197    /**
198    * An attachment waiting to be deleted at next document save.
199    *
200    * @version $Id: fca742e81f140ea5f8b5504201aab780b394a788 $
201    * @since 5.2M1
202    */
 
203    public static class XWikiAttachmentToRemove
204    {
205    /**
206    * @see #getAttachment()
207    */
208    private XWikiAttachment attachment;
209   
210    /**
211    * @see #isToRecycleBin()
212    */
213    private boolean toRecycleBin;
214   
215    /**
216    * @param attachment the attachment to delete
217    * @param toRecycleBin true of the attachment should be moved to the recycle bin
218    */
 
219  8 toggle public XWikiAttachmentToRemove(XWikiAttachment attachment, boolean toRecycleBin)
220    {
221  8 this.attachment = attachment;
222  8 this.toRecycleBin = toRecycleBin;
223    }
224   
225    /**
226    * @return the attachment to delete
227    */
 
228  10 toggle public XWikiAttachment getAttachment()
229    {
230  10 return this.attachment;
231    }
232   
233    /**
234    * @return true of the attachment should be moved to the recycle bin
235    */
 
236  5 toggle public boolean isToRecycleBin()
237    {
238  5 return this.toRecycleBin;
239    }
240   
 
241  0 toggle @Override
242    public int hashCode()
243    {
244  0 return this.attachment.hashCode();
245    }
246   
 
247  0 toggle @Override
248    public boolean equals(Object obj)
249    {
250  0 if (obj instanceof XWikiAttachmentToRemove) {
251  0 return this.attachment.equals(((XWikiAttachmentToRemove) obj).getAttachment());
252    }
253   
254  0 return false;
255    }
256   
 
257  0 toggle @Override
258    public String toString()
259    {
260  0 return this.attachment.toString();
261    }
262    }
263   
264    /**
265    * Regex Pattern to recognize if there's HTML code in a XWiki page.
266    */
267    private static final Pattern HTML_TAG_PATTERN = Pattern.compile(
268    "</?+(html|img|a|i|br?|embed|script|form|input|textarea|object|font|li|[dou]l|table|center|hr|p) ?([^>]*+)>");
269   
270    /**
271    * Format for passing xproperties references in URLs. General format:
272    * {@code &lt;space&gt;.&lt;pageClass&gt;_&lt;number&gt;_&lt;propertyName&gt;} (e.g.
273    * {@code XWiki.XWikiRights_0_member}).
274    */
275    private static final Pattern XPROPERTY_REFERENCE_PATTERN = Pattern.compile("(.+?)_([0-9]+)_(.+)");
276   
277    public static final EntityReference COMMENTSCLASS_REFERENCE = new LocalDocumentReference("XWiki", "XWikiComments");
278   
279    public static final EntityReference SHEETCLASS_REFERENCE = new LocalDocumentReference("XWiki", "SheetClass");
280   
281    public static final int HAS_ATTACHMENTS = 1;
282   
283    public static final int HAS_OBJECTS = 2;
284   
285    public static final int HAS_CLASS = 4;
286   
287    /**
288    * The name of the key in the XWikiContext which contains the document used to check for programming rights.
289    */
290    public static final String CKEY_SDOC = "sdoc";
291   
292    /**
293    * Separator string between database name and space name.
294    */
295    public static final String DB_SPACE_SEP = ":";
296   
297    /**
298    * Separator string between space name and page name.
299    */
300    public static final String SPACE_NAME_SEP = ".";
301   
302    private static final LocalStringEntityReferenceSerializer LOCAL_REFERENCE_SERIALIZER =
303    new LocalStringEntityReferenceSerializer(new DefaultSymbolScheme());
304   
305    /**
306    * Used to resolve a string into a proper Document Reference using the current document's reference to fill the
307    * blanks.
308    */
 
309  159 toggle private static DocumentReferenceResolver<String> getCurrentDocumentReferenceResolver()
310    {
311  159 return Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, "current");
312    }
313   
314    /**
315    * Used to resolve a ResourceReference into a proper Entity Reference using the current document to fill the blanks.
316    */
 
317  47 toggle private static EntityReferenceResolver<ResourceReference> getResourceReferenceEntityReferenceResolver()
318    {
319  47 return Utils
320    .getComponent(new DefaultParameterizedType(null, EntityReferenceResolver.class, ResourceReference.class));
321    }
322   
 
323  20327 toggle private static EntityReferenceResolver<String> getXClassEntityReferenceResolver()
324    {
325  20324 return Utils.getComponent(EntityReferenceResolver.TYPE_STRING, "xclass");
326    }
327   
328    /**
329    * Used to resolve a string into a proper Document Reference using the current document's reference to fill the
330    * blanks, except for the page name for which the default page name is used instead and for the wiki name for which
331    * the current wiki is used instead of the current document reference's wiki.
332    */
 
333  24601 toggle private static DocumentReferenceResolver<String> getCurrentMixedDocumentReferenceResolver()
334    {
335  24603 return Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, "currentmixed");
336    }
337   
338    /**
339    * Used to normalize references.
340    */
 
341  103576 toggle private static DocumentReferenceResolver<EntityReference> getCurrentReferenceDocumentReferenceResolver()
342    {
343  103570 return Utils.getComponent(DocumentReferenceResolver.TYPE_REFERENCE, "current");
344    }
345   
346    /**
347    * Used to resolve parent references in the way they are stored externally (database, xml, etc), ie relative or
348    * absolute.
349    */
 
350  10737 toggle private static EntityReferenceResolver<String> getRelativeEntityReferenceResolver()
351    {
352  10737 return Utils.getComponent(EntityReferenceResolver.TYPE_STRING, "relative");
353    }
354   
355    /**
356    * Used to convert a proper Document Reference to string (compact form).
357    */
 
358  9 toggle private static EntityReferenceSerializer<String> getCompactEntityReferenceSerializer()
359    {
360  9 return Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, "compact");
361    }
362   
363    /**
364    * Used to convert a Document Reference to string (compact form without the wiki part if it matches the current
365    * wiki).
366    */
 
367  50694 toggle private static EntityReferenceSerializer<String> getCompactWikiEntityReferenceSerializer()
368    {
369  50694 return Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, "compactwiki");
370    }
371   
372    /**
373    * Used to normalize references.
374    */
 
375  6718 toggle private static ObjectReferenceResolver<EntityReference> getCurrentReferenceObjectReferenceResolver()
376    {
377  6713 return Utils.getComponent(ObjectReferenceResolver.TYPE_REFERENCE, "current");
378    }
379   
380    private String title;
381   
382    /**
383    * Reference to this document's parent.
384    * <p>
385    * Note that we're saving the parent reference as a relative reference instead of an absolute one because We want
386    * the ability (for example) to create a parent reference relative to the current space or wiki so that a copy of
387    * this XWikiDocument object would retain that relativity. This is for example useful when copying a Wiki into
388    * another Wiki so that the copied XWikiDcoument's parent reference points to the new wiki.
389    */
390    private EntityReference parentReference;
391   
392    private DocumentReference documentReference;
393   
394    private String content;
395   
396    private String meta;
397   
398    private String format;
399   
400    /**
401    * First author of the document.
402    */
403    private DocumentReference creatorReference;
404   
405    /**
406    * The Author is changed when any part of the document changes (content, objects, attachments).
407    */
408    private DocumentReference authorReference;
409   
410    /**
411    * The last user that has changed the document's content (ie not object, attachments). The Content author is only
412    * changed when the document content changes. Note that Content Author is used to check programming rights on a
413    * document and this is the reason we need to know the last author who's modified the content since programming
414    * rights depend on this.
415    */
416    private DocumentReference contentAuthorReference;
417   
418    private String customClass;
419   
420    private Date contentUpdateDate;
421   
422    private Date updateDate;
423   
424    private Date creationDate;
425   
426    protected Version version;
427   
428    private long id = 0;
429   
430    private boolean mostRecent = true;
431   
432    private boolean isNew = true;
433   
434    /**
435    * The reference to the document that is the template for the current document.
436    *
437    * @todo this field is not used yet since it's not currently saved in the database.
438    */
439    private DocumentReference templateDocumentReference;
440   
441    private Locale locale;
442   
443    private Locale defaultLocale;
444   
445    /**
446    * Indicates whether the document is 'hidden', meaning that it should not be returned in public search results.
447    * WARNING: this is a temporary hack until the new data model is designed and implemented. No code should rely on or
448    * use this property, since it will be replaced with a generic metadata.
449    */
450    private boolean hidden = false;
451   
452    /**
453    * Comment on the latest modification.
454    */
455    private String comment;
456   
457    /**
458    * Wiki syntax supported by this document. This is used to support different syntaxes inside the same wiki. For
459    * example a page can use the MediaWiki 1.0 syntax while another one uses the XWiki 2.1 syntax.
460    */
461    private Syntax syntax;
462   
463    /**
464    * Is latest modification a minor edit.
465    */
466    private boolean isMinorEdit = false;
467   
468    /**
469    * Used to make sure the MetaData String is regenerated.
470    */
471    private boolean isContentDirty = true;
472   
473    /**
474    * Used to make sure the MetaData String is regenerated.
475    */
476    private boolean isMetaDataDirty = true;
477   
478    private int elements = HAS_OBJECTS | HAS_ATTACHMENTS;
479   
480    // Meta Data
481    private BaseClass xClass;
482   
483    private String xClassXML;
484   
485    /**
486    * Map holding document objects indexed by XClass references (i.e. Document References since a XClass reference
487    * points to a document). The map is not synchronized, and uses a TreeMap implementation to preserve index ordering
488    * (consistent sorted order for output to XML, rendering in velocity, etc.)
489    */
490    private Map<DocumentReference, List<BaseObject>> xObjects = new TreeMap<DocumentReference, List<BaseObject>>();
491   
492    private final List<XWikiAttachment> attachmentList =
493    new AbstractNotifyOnUpdateList<XWikiAttachment>(new ArrayList<XWikiAttachment>())
494    {
 
495  88678 toggle @Override
496    public void onUpdate()
497    {
498  88674 setMetaDataDirty(true);
499    }
500    };
501   
502    // Caching
503    private boolean fromCache = false;
504   
505    private List<BaseObject> xObjectsToRemove = new ArrayList<BaseObject>();
506   
507    private List<XWikiAttachmentToRemove> attachmentsToRemove = new ArrayList<XWikiAttachmentToRemove>();
508   
509    /**
510    * The view template (vm file) to use. When not set the default view template is used.
511    *
512    * @see com.xpn.xwiki.web.ViewAction#render(XWikiContext)
513    */
514    private String defaultTemplate;
515   
516    private String validationScript;
517   
518    private Object wikiNode;
519   
520    /**
521    * We are using a SoftReference which will allow the archive to be discarded by the Garbage collector as long as the
522    * context is closed (usually during the request)
523    */
524    private SoftReference<XWikiDocumentArchive> archive;
525   
526    private XWikiStoreInterface store;
527   
528    /**
529    * @see #getOriginalDocument()
530    */
531    private XWikiDocument originalDocument;
532   
533    /**
534    * Used to display the title and the content of this document. Do not inject the component here to avoid any simple
535    * new XWikiDocument to cause many useless initialization, in particular, during initialization of the stub context
536    * and other fake documents in tests.
537    */
538    private DocumentDisplayer documentDisplayer;
539   
540    /**
541    * @see #getDefaultEntityReferenceSerializer()
542    */
543    private EntityReferenceSerializer<String> defaultEntityReferenceSerializer;
544   
545    /**
546    * @see #getExplicitDocumentReferenceResolver()
547    */
548    private DocumentReferenceResolver<String> explicitDocumentReferenceResolver;
549   
550    /**
551    * @see #getExplicitReferenceDocumentReferenceResolver()
552    */
553    private DocumentReferenceResolver<EntityReference> explicitReferenceDocumentReferenceResolver;
554   
555    /**
556    * @see #getUidStringEntityReferenceSerializer()
557    */
558    private EntityReferenceSerializer<String> uidStringEntityReferenceSerializer;
559   
560    private Provider<OldRendering> oldRenderingProvider;
561   
562    private JobProgressManager progress;
563   
564    /**
565    * @see #getSyntaxFactory()
566    */
567    private SyntaxFactory syntaxFactory;
568   
569    private ContextualLocalizationManager localization;
570   
571    /**
572    * The document structure expressed as a tree of Block objects. We store it for performance reasons since parsing is
573    * a costly operation that we don't want to repeat whenever some code ask for the XDOM information.
574    */
575    private XDOM xdomCache;
576   
577    /**
578    * Use to store rendered documents in #getRenderedContent(). Do not inject the component here to avoid any simple
579    * new XWikiDocument to cause many useless initialization, in particular, during initialization of the stub context
580    * and other fake documents in tests.
581    */
582    private RenderingCache renderingCache;
583   
584    /**
585    * Cache the parent reference resolved as an absolute reference for improved performance (so that we don't have to
586    * resolve the relative reference every time getParentReference() is called.
587    */
588    private DocumentReference parentReferenceCache;
589   
590    /**
591    * @see #getKey()
592    */
593    private String keyCache;
594   
595    /**
596    * @see #getLocalKey()
597    */
598    private String localKeyCache;
599   
600    private RenderingContext renderingContext;
601   
602    /**
603    * @since 2.2M1
604    */
 
605  250351 toggle public XWikiDocument(DocumentReference reference)
606    {
607  250352 init(reference);
608    }
609   
610    /**
611    * @since 6.2
612    */
 
613  1057350 toggle public XWikiDocument(DocumentReference reference, Locale locale)
614    {
615  1057334 init(reference);
616   
617  1057324 this.locale = locale;
618    }
619   
620    /**
621    * @deprecated since 2.2M1 use {@link #XWikiDocument(org.xwiki.model.reference.DocumentReference)} instead
622    */
 
623  21888 toggle @Deprecated
624    public XWikiDocument()
625    {
626  21888 this(null);
627    }
628   
629    /**
630    * Constructor that specifies the local document identifier: space name, document name. {@link #setDatabase(String)}
631    * must be called afterwards to specify the wiki name.
632    *
633    * @param space the space this document belongs to
634    * @param name the name of the document
635    * @deprecated since 2.2M1 use {@link #XWikiDocument(org.xwiki.model.reference.DocumentReference)} instead
636    */
 
637  86 toggle @Deprecated
638    public XWikiDocument(String space, String name)
639    {
640  86 this(null, space, name);
641    }
642   
643    /**
644    * Constructor that specifies the full document identifier: wiki name, space name, document name.
645    *
646    * @param wiki The wiki this document belongs to.
647    * @param space The space this document belongs to.
648    * @param name The name of the document (can contain either the page name or the space and page name)
649    * @deprecated since 2.2M1 use {@link #XWikiDocument(org.xwiki.model.reference.DocumentReference)} instead
650    */
 
651  88 toggle @Deprecated
652    public XWikiDocument(String wiki, String space, String name)
653    {
654    // We allow to specify the space in the name (eg name = "space.page"). In this case the passed space is
655    // ignored.
656   
657    // Build an entity reference that will serve as a current context reference against which to resolve if the
658    // passed name doesn't contain a space.
659  88 EntityReference contextReference = null;
660  88 if (!StringUtils.isEmpty(space)) {
661  88 contextReference = new EntityReference(space, EntityType.SPACE);
662    }
663   
664  88 DocumentReference reference = getCurrentDocumentReferenceResolver().resolve(name, contextReference);
665   
666  88 if (!StringUtils.isEmpty(wiki)) {
667  2 reference = reference.replaceParent(reference.getWikiReference(), new WikiReference(wiki));
668    }
669   
670  88 init(reference);
671    }
672   
673    /**
674    * Used to resolve a string into a proper Document Reference.
675    */
 
676  39629 toggle private DocumentReferenceResolver<String> getExplicitDocumentReferenceResolver()
677    {
678  39625 if (this.explicitDocumentReferenceResolver == null) {
679  18430 this.explicitDocumentReferenceResolver =
680    Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, "explicit");
681    }
682   
683  39625 return this.explicitDocumentReferenceResolver;
684    }
685   
686    /**
687    * Used to normalize references.
688    */
 
689  20058 toggle private DocumentReferenceResolver<EntityReference> getExplicitReferenceDocumentReferenceResolver()
690    {
691  20058 if (this.explicitReferenceDocumentReferenceResolver == null) {
692  7470 this.explicitReferenceDocumentReferenceResolver =
693    Utils.getComponent(DocumentReferenceResolver.TYPE_REFERENCE, "explicit");
694    }
695   
696  20059 return this.explicitReferenceDocumentReferenceResolver;
697    }
698   
699    /**
700    * Used to convert a proper Document Reference to string (standard form).
701    */
 
702  58102 toggle private EntityReferenceSerializer<String> getDefaultEntityReferenceSerializer()
703    {
704  58102 if (this.defaultEntityReferenceSerializer == null) {
705  34140 this.defaultEntityReferenceSerializer = Utils.getComponent(EntityReferenceSerializer.TYPE_STRING);
706    }
707   
708  58102 return this.defaultEntityReferenceSerializer;
709    }
710   
711    /**
712    * Used to compute document identifier.
713    */
 
714  8 toggle private EntityReferenceSerializer<String> getUidStringEntityReferenceSerializer()
715    {
716  8 if (this.uidStringEntityReferenceSerializer == null) {
717  8 this.uidStringEntityReferenceSerializer = Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, "uid");
718    }
719   
720  8 return this.uidStringEntityReferenceSerializer;
721    }
722   
723    /**
724    * Used to create proper {@link Syntax} objects.
725    */
 
726  4119 toggle private SyntaxFactory getSyntaxFactory()
727    {
728  4118 if (this.syntaxFactory == null) {
729  3781 this.syntaxFactory = Utils.getComponent((Type) SyntaxFactory.class);
730    }
731   
732  4118 return this.syntaxFactory;
733    }
734   
 
735  5 toggle private ContextualLocalizationManager getLocalization()
736    {
737  5 if (this.localization == null) {
738  1 this.localization = Utils.getComponent(ContextualLocalizationManager.class);
739    }
740   
741  5 return this.localization;
742    }
743   
 
744  3 toggle private OldRendering getOldRendering()
745    {
746  3 if (this.oldRenderingProvider == null) {
747  1 this.oldRenderingProvider = Utils.getComponent(OldRendering.TYPE_PROVIDER);
748    }
749   
750  3 return this.oldRenderingProvider.get();
751    }
752   
 
753  43827 toggle private JobProgressManager getProgress()
754    {
755  43827 if (this.progress == null) {
756  799 this.progress = Utils.getComponent(JobProgressManager.class);
757    }
758   
759  43825 return this.progress;
760    }
761   
 
762  5 toggle private String localizePlainOrKey(String key, Object... parameters)
763    {
764  5 return StringUtils.defaultString(getLocalization().getTranslationPlain(key, parameters), key);
765    }
766   
 
767  2639 toggle public XWikiStoreInterface getStore(XWikiContext context)
768    {
769  2639 return context.getWiki().getStore();
770    }
771   
 
772  0 toggle public XWikiAttachmentStoreInterface getAttachmentStore(XWikiContext context)
773    {
774  0 return context.getWiki().getAttachmentStore();
775    }
776   
 
777  6410 toggle public XWikiVersioningStoreInterface getVersioningStore(XWikiContext context)
778    {
779  6410 return context.getWiki().getVersioningStore();
780    }
781   
 
782  37404 toggle public XWikiStoreInterface getStore()
783    {
784  37406 return this.store;
785    }
786   
 
787  489974 toggle public void setStore(XWikiStoreInterface store)
788    {
789  489953 this.store = store;
790    }
791   
 
792  16791 toggle private RenderingContext getRenderingContext()
793    {
794  16793 if (this.renderingContext == null) {
795  7716 this.renderingContext = Utils.getComponent(RenderingContext.class);
796    }
797   
798  16790 return this.renderingContext;
799    }
800   
801    /**
802    * Helper to produce and cache a local uid serialization of this document reference, including the language. Only
803    * translated document will have language appended.
804    *
805    * @return a unique name (in a wiki) (5:space4:name2:lg)
806    */
 
807  37736 toggle private String getLocalKey()
808    {
809  37736 if (this.localKeyCache == null) {
810  15091 this.localKeyCache =
811    LocalUidStringEntityReferenceSerializer.INSTANCE.serialize(getDocumentReferenceWithLocale());
812    }
813   
814  37740 return this.localKeyCache;
815    }
816   
817    /**
818    * Helper to produce and cache a uid serialization of this document reference, including the language. Only
819    * translated document will have language appended.
820    *
821    * @return a unique name (8:wikiname5:space4:name2:lg or 8:wikiname5:space4:name)
822    * @since 4.0M1
823    */
 
824  8 toggle public String getKey()
825    {
826  8 if (this.keyCache == null) {
827  8 this.keyCache = getUidStringEntityReferenceSerializer().serialize(getDocumentReferenceWithLocale());
828    }
829   
830  8 return this.keyCache;
831    }
832   
 
833  440 toggle @Override
834    public int hashCode()
835    {
836  440 return (int) Util.getHash(getLocalKey());
837    }
838   
839    /**
840    * @return the unique id used to represent the document, as a number. This id is technical and is equivalent to the
841    * Document Reference + the language of the Document. This technical id should only be used for the storage
842    * layer and all user APIs should instead use Document Reference and language as they are model-related
843    * while the id isn't (it's purely technical).
844    */
 
845  37310 toggle public long getId()
846    {
847    // TODO: Ensure uniqueness of the generated id
848    // The implementation doesn't guarantee a unique id since it uses a hashing method which never guarantee
849    // uniqueness. However, the hash algorithm is really unlikely to collide in a given wiki. This needs to be
850    // fixed to produce a real unique id since otherwise we can have clashes in the database.
851   
852    // Note: We don't use the wiki name in the document id's computation. The main historical reason is so
853    // that all things saved in a given wiki's database are always stored relative to that wiki so that
854    // changing that wiki's name is simpler.
855   
856  37307 this.id = Util.getHash(getLocalKey());
857   
858  37301 return this.id;
859    }
860   
861    /**
862    * @see #getId()
863    */
 
864  14459 toggle public void setId(long id)
865    {
866  14468 this.id = id;
867    }
868   
869    /**
870    * Return the full local space reference. For example a document located in sub-space <code>space11</code> of space
871    * <code>space1</code> will return <code>space1.space11</code>.
872    * <p>
873    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
874    *
875    * @return the local reference the space of the document as String
876    * @deprecated since 2.2M1 used {@link #getDocumentReference()} instead
877    */
 
878  73865 toggle @Deprecated
879    public String getSpace()
880    {
881  73863 return LOCAL_REFERENCE_SERIALIZER.serialize(getDocumentReference().getLastSpaceReference());
882    }
883   
884    /**
885    * Set the full local space reference.
886    * <p>
887    * Note that this method cannot be removed for now since it's used by Hibernate for loading a XWikiDocument.
888    *
889    * @see #getSpace()
890    * @deprecated since 2.2M1 used {@link #setDocumentReference(DocumentReference)} instead
891    */
 
892  7286 toggle @Deprecated
893    public void setSpace(String spaces)
894    {
895  7287 if (spaces != null) {
896  7287 DocumentReference reference = getDocumentReference();
897  7287 EntityReference spaceReference = getRelativeEntityReferenceResolver().resolve(spaces, EntityType.SPACE);
898  7289 spaceReference = spaceReference.appendParent(getDocumentReference().getWikiReference());
899  7289 setDocumentReferenceInternal(
900    new DocumentReference(reference.getName(), new SpaceReference(spaceReference)));
901    }
902    }
903   
904    /**
905    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
906    *
907    * @return the name of the space of the document
908    * @see #getSpace()
909    * @deprecated use {@link #getDocumentReference()} instead
910    */
 
911  7972 toggle @Deprecated
912    public String getWeb()
913    {
914  7972 return getSpace();
915    }
916   
917    /**
918    * Note that this method cannot be removed for now since it's used by Hibernate for loading a XWikiDocument.
919    *
920    * @deprecated use {@link #setDocumentReference(DocumentReference)} instead
921    */
 
922  3642 toggle @Deprecated
923    public void setWeb(String space)
924    {
925  3643 setSpace(space);
926    }
927   
 
928  47273 toggle @Override
929    public String getVersion()
930    {
931  47273 return getRCSVersion().toString();
932    }
933   
 
934  5765 toggle public void setVersion(String version)
935    {
936  5765 if (!StringUtils.isEmpty(version)) {
937  5765 this.version = new Version(version);
938    }
939    }
940   
 
941  55987 toggle public Version getRCSVersion()
942    {
943  55987 if (this.version == null) {
944  733 return new Version("1.1");
945    }
946  55254 return this.version;
947    }
948   
 
949  5769 toggle public void setRCSVersion(Version version)
950    {
951  5769 this.version = version;
952    }
953   
954    /**
955    * @return the copy of this XWikiDocument instance before any modification was made to it. It is reset to the actual
956    * values when the document is saved in the database. This copy is used for finding out differences made to
957    * this document (useful for example to send the correct notifications to document change listeners).
958    */
 
959  33147 toggle @Override
960    public XWikiDocument getOriginalDocument()
961    {
962  33147 return this.originalDocument;
963    }
964   
965    /**
966    * @param originalDocument the original document representing this document instance before any change was made to
967    * it, prior to the last time it was saved
968    * @see #getOriginalDocument()
969    */
 
970  454398 toggle public void setOriginalDocument(XWikiDocument originalDocument)
971    {
972  454390 this.originalDocument = originalDocument;
973    }
974   
975    /**
976    * @return the parent reference or null if the parent is not set
977    * @since 2.2M1
978    */
 
979  20928 toggle public DocumentReference getParentReference()
980    {
981    // Ensure we always return absolute document references for the parent since we always want well-constructed
982    // references and since we store the parent reference as relative internally.
983  20929 if (this.parentReferenceCache == null && getRelativeParentReference() != null) {
984  5230 this.parentReferenceCache = getExplicitReferenceDocumentReferenceResolver()
985    .resolve(getRelativeParentReference(), getDocumentReference());
986    }
987   
988  20930 return this.parentReferenceCache;
989    }
990   
991    /**
992    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
993    *
994    * @return the parent reference stored in the database, which is relative to this document, or an empty string ("")
995    * if the parent is not set
996    * @see #getParentReference()
997    * @deprecated since 2.2M1 use {@link #getParentReference()} instead
998    */
 
999  15650 toggle @Deprecated
1000    public String getParent()
1001    {
1002  15650 String parentReferenceAsString;
1003  15652 if (getParentReference() != null) {
1004  8647 parentReferenceAsString = getDefaultEntityReferenceSerializer().serialize(getRelativeParentReference());
1005    } else {
1006  7004 parentReferenceAsString = "";
1007    }
1008  15651 return parentReferenceAsString;
1009    }
1010   
1011    /**
1012    * @deprecated since 2.2M1 use {@link #getParentReference()} instead
1013    */
 
1014  0 toggle @Deprecated
1015    public XWikiDocument getParentDoc()
1016    {
1017  0 return new XWikiDocument(getParentReference());
1018    }
1019   
1020    /**
1021    * @since 2.2.3
1022    */
 
1023  43853 toggle public void setParentReference(EntityReference parentReference)
1024    {
1025  43852 if (!Objects.equals(getRelativeParentReference(), parentReference)) {
1026  24922 this.parentReference = parentReference;
1027   
1028    // Clean the absolute parent reference cache to rebuild it next time getParentReference is called.
1029  24919 this.parentReferenceCache = null;
1030   
1031  24920 setMetaDataDirty(true);
1032    }
1033    }
1034   
1035    /**
1036    * Note that this method cannot be removed for now since it's used by Hibernate for loading a XWikiDocument.
1037    *
1038    * @param parent the reference of the parent relative to the document
1039    * @deprecated since 2.2M1, use {@link #setParentReference(EntityReference)} instead
1040    */
 
1041  3778 toggle @Deprecated
1042    public void setParent(String parent)
1043    {
1044    // If the passed parent is an empty string we also need to set the reference to null. The reason is that
1045    // in the database we store "" when the parent is empty and thus when Hibernate loads this class it'll call
1046    // setParent with "" if the parent had not been set when saved.
1047  3779 if (StringUtils.isEmpty(parent)) {
1048  330 setParentReference((EntityReference) null);
1049    } else {
1050  3448 setParentReference(getRelativeEntityReferenceResolver().resolve(parent, EntityType.DOCUMENT));
1051    }
1052    }
1053   
 
1054  67830 toggle @Override
1055    public String getContent()
1056    {
1057  67830 return this.content;
1058    }
1059   
 
1060  38314 toggle public void setContent(String content)
1061    {
1062  38317 if (content == null) {
1063  0 content = "";
1064    }
1065   
1066  38316 boolean notEqual = !content.equals(this.content);
1067   
1068  38318 this.content = content;
1069   
1070  38314 if (notEqual) {
1071    // invalidate parsed xdom
1072  13983 this.xdomCache = null;
1073  13983 setContentDirty(true);
1074  13983 setWikiNode(null);
1075    }
1076    }
1077   
 
1078  5 toggle public void setContent(XDOM content) throws XWikiException
1079    {
1080  5 setContent(renderXDOM(content, getSyntax()));
1081    }
1082   
1083    /**
1084    * @return the default rendering cache
1085    */
 
1086  12384 toggle private RenderingCache getRenderingCache()
1087    {
1088  12385 if (this.renderingCache == null) {
1089  667 this.renderingCache = Utils.getComponent((Type) RenderingCache.class);
1090    }
1091  12384 return this.renderingCache;
1092    }
1093   
1094    /**
1095    * @return the configured document displayer
1096    */
 
1097  25899 toggle private DocumentDisplayer getDocumentDisplayer()
1098    {
1099  25901 if (this.documentDisplayer == null) {
1100  9444 this.documentDisplayer = Utils.getComponent((Type) DocumentDisplayer.class, "configured");
1101    }
1102  25898 return this.documentDisplayer;
1103    }
1104   
 
1105  16790 toggle private Syntax getOutputSyntax()
1106    {
1107  16792 return getRenderingContext().getTargetSyntax();
1108    }
1109   
1110    /**
1111    * Parse, execute and render the document.
1112    *
1113    * @param targetSyntax the syntax to use to render the document
1114    * @param executionContextIsolated see {@link DocumentDisplayerParameters#isExecutionContextIsolated()}
1115    * @param transformationContextIsolated see {@link DocumentDisplayerParameters#isTransformationContextIsolated()}
1116    * @param transformationContextRestricted see
1117    * {@link DocumentDisplayerParameters#isTransformationContextRestricted()}
1118    * @param translate get translated content of the document
1119    * @return the result of the document execution rendered in the passed syntax
1120    * @throws XWikiException when failing to display the document
1121    */
 
1122  6194 toggle private String display(Syntax targetSyntax, boolean executionContextIsolated, boolean transformationContextIsolated,
1123    boolean transformationContextRestricted, boolean translate) throws XWikiException
1124    {
1125    // Note: We are currently duplicating code from getRendered signature because some calling
1126    // code is expecting that the rendering will happen in the calling document's context and not in this
1127    // document's context. For example this is true for the Admin page, see
1128    // http://jira.xwiki.org/jira/browse/XWIKI-4274 for more details.
1129   
1130  6194 getProgress().startStep(this, "document.progress.render", "Render document [{}] in syntax [{}]",
1131    getDocumentReference(), targetSyntax);
1132   
1133  6194 try {
1134  6193 getProgress().pushLevelProgress(3, getDocumentReference());
1135   
1136  6194 getProgress().startStep(getDocumentReference(), "document.progress.render.translatedcontent",
1137    "Get translated content");
1138   
1139  6194 XWikiContext xcontext = getXWikiContext();
1140   
1141  6194 XWikiDocument tdoc = translate ? getTranslatedDocument(xcontext) : this;
1142  6194 String translatedContent = tdoc.getContent();
1143   
1144  6194 getProgress().startStep(getDocumentReference(), "document.progress.render.cache",
1145    "Try to get content from the cache");
1146   
1147  6194 String renderedContent = getRenderingCache().getRenderedContent(tdoc.getDocumentReferenceWithLocale(),
1148    translatedContent, xcontext);
1149   
1150  6193 if (renderedContent == null) {
1151  6193 getProgress().startStep(getDocumentReference(), "document.progress.render.execute", "Execute content");
1152   
1153    // Configure display
1154  6193 DocumentDisplayerParameters parameters = new DocumentDisplayerParameters();
1155  6193 parameters.setExecutionContextIsolated(executionContextIsolated);
1156  6193 parameters.setTransformationContextIsolated(transformationContextIsolated);
1157  6193 parameters.setTransformationContextRestricted(transformationContextRestricted);
1158    // Render the translated content (matching the current language) using this document's syntax.
1159  6194 parameters.setContentTranslated(tdoc != this);
1160  6194 parameters.setTargetSyntax(targetSyntax);
1161   
1162    // Execute display
1163  6193 Test failure here XDOM contentXDOM = getDocumentDisplayer().display(this, parameters);
1164   
1165    // Render the result
1166  6191 renderedContent = renderXDOM(contentXDOM, targetSyntax);
1167   
1168  6191 getRenderingCache().setRenderedContent(getDocumentReference(), translatedContent, renderedContent,
1169    xcontext);
1170    }
1171   
1172  6191 return renderedContent;
1173    } finally {
1174  6194 getProgress().popLevelProgress(getDocumentReference());
1175  6193 getProgress().endStep(this);
1176    }
1177    }
1178   
 
1179  5808 toggle public String getRenderedContent(Syntax targetSyntax, XWikiContext context) throws XWikiException
1180    {
1181  5808 Test failure here return getRenderedContent(targetSyntax, true, context);
1182    }
1183   
1184    /**
1185    * @since 8.4RC1
1186    */
 
1187  0 toggle public String getRenderedContent(boolean transformationContextIsolated, XWikiContext context) throws XWikiException
1188    {
1189  0 return getRenderedContent(getOutputSyntax(), transformationContextIsolated, context);
1190    }
1191   
 
1192  5957 toggle public String getRenderedContent(Syntax targetSyntax, boolean transformationContextIsolated, XWikiContext context)
1193    throws XWikiException
1194    {
1195    // Make sure the context secure document is the current document so that it's executed with its own
1196    // rights
1197  5957 Object currrentSdoc = context.get("sdoc");
1198  5959 try {
1199    // If we execute a translation use translated document as secure document
1200  5959 XWikiDocument tdoc = getTranslatedDocument(context);
1201   
1202  5959 context.put("sdoc", tdoc);
1203   
1204  5959 Test failure here return display(targetSyntax, false, transformationContextIsolated, false, true);
1205    } finally {
1206  5958 context.put("sdoc", currrentSdoc);
1207    }
1208    }
1209   
 
1210  602 toggle public String getRenderedContent(XWikiContext context) throws XWikiException
1211    {
1212  601 Test failure here return getRenderedContent(getOutputSyntax(), context);
1213    }
1214   
1215    /**
1216    * @param text the text to render
1217    * @param syntaxId the id of the Syntax used by the passed text (e.g. {@code xwiki/2.1})
1218    * @param context the XWiki Context object
1219    * @return the given text rendered in the context of this document using the passed Syntax
1220    * @since 1.6M1
1221    */
 
1222  200 toggle public String getRenderedContent(String text, String syntaxId, XWikiContext context)
1223    {
1224  200 return getRenderedContent(text, syntaxId, getOutputSyntax().toIdString(), context);
1225    }
1226   
1227    /**
1228    * @param text the text to render
1229    * @param syntaxId the id of the Syntax used by the passed text (e.g. {@code xwiki/2.1})
1230    * @param restrictedTransformationContext see {@link DocumentDisplayerParameters#isTransformationContextRestricted}.
1231    * @param context the XWiki Context object
1232    * @return the given text rendered in the context of this document using the passed Syntax
1233    * @since 4.2M1
1234    */
 
1235  0 toggle public String getRenderedContent(String text, String syntaxId, boolean restrictedTransformationContext,
1236    XWikiContext context)
1237    {
1238  0 return getRenderedContent(text, syntaxId, getOutputSyntax().toIdString(), restrictedTransformationContext,
1239    context);
1240    }
1241   
1242    /**
1243    * @param text the text to render
1244    * @param syntaxId the id of the Syntax used by the passed text (e.g. {@code xwiki/2.1})
1245    * @param restrictedTransformationContext see {@link DocumentDisplayerParameters#isTransformationContextRestricted}.
1246    * @param sDocument the {@link XWikiDocument} to use as secure document, if null keep the current one
1247    * @param context the XWiki Context object
1248    * @return the given text rendered in the context of this document using the passed Syntax
1249    * @since 8.3
1250    */
 
1251  31 toggle public String getRenderedContent(String text, String syntaxId, boolean restrictedTransformationContext,
1252    XWikiDocument sDocument, XWikiContext context)
1253    {
1254  31 return getRenderedContent(text, syntaxId, getOutputSyntax().toIdString(), restrictedTransformationContext,
1255    sDocument, context);
1256    }
1257   
1258    /**
1259    * @param text the text to render
1260    * @param sourceSyntaxId the id of the Syntax used by the passed text (e.g. {@code xwiki/2.1})
1261    * @param targetSyntaxId the id of the syntax in which to render the document content
1262    * @param context the XWiki context
1263    * @return the given text rendered in the context of this document using the passed Syntax
1264    * @since 2.0M3
1265    */
 
1266  200 toggle public String getRenderedContent(String text, String sourceSyntaxId, String targetSyntaxId, XWikiContext context)
1267    {
1268  200 return getRenderedContent(text, sourceSyntaxId, targetSyntaxId, false, context);
1269    }
1270   
1271    /**
1272    * @param text the text to render
1273    * @param sourceSyntaxId the id of the Syntax used by the passed text (e.g. {@code xwiki/2.1})
1274    * @param targetSyntaxId the id of the syntax in which to render the document content
1275    * @param restrictedTransformationContext see {@link DocumentDisplayerParameters#isTransformationContextRestricted}.
1276    * @param context the XWiki context
1277    * @return the given text rendered in the context of this document using the passed Syntax
1278    * @since 4.2M1
1279    */
 
1280  200 toggle public String getRenderedContent(String text, String sourceSyntaxId, String targetSyntaxId,
1281    boolean restrictedTransformationContext, XWikiContext context)
1282    {
1283  200 return getRenderedContent(text, sourceSyntaxId, targetSyntaxId, restrictedTransformationContext, null, context);
1284    }
1285   
1286    /**
1287    * @param text the text to render
1288    * @param sourceSyntaxId the id of the Syntax used by the passed text (e.g. {@code xwiki/2.1})
1289    * @param targetSyntaxId the id of the syntax in which to render the document content
1290    * @param restrictedTransformationContext see {@link DocumentDisplayerParameters#isTransformationContextRestricted}.
1291    * @param sDocument the {@link XWikiDocument} to use as secure document, if null keep the current one
1292    * @param context the XWiki context
1293    * @return the given text rendered in the context of this document using the passed Syntax
1294    * @since 8.3
1295    */
 
1296  235 toggle public String getRenderedContent(String text, String sourceSyntaxId, String targetSyntaxId,
1297    boolean restrictedTransformationContext, XWikiDocument sDocument, XWikiContext context)
1298    {
1299  235 Map<String, Object> backup = null;
1300   
1301  235 getProgress().startStep(this, "document.progress.renderText",
1302    "Execute content [{}] in the context of document [{}]",
1303  235 StringUtils.substring(text, 0, 100) + (text.length() >= 100 ? "..." : ""), getDocumentReference());
1304   
1305  235 XWikiDocument currentSDocument = (XWikiDocument) context.get(CKEY_SDOC);
1306  235 try {
1307    // We have to render the given text in the context of this document. Check if this document is already
1308    // on the context (same Java object reference). We don't check if the document references are equal
1309    // because this document can have temporary changes that are not present on the context document even if
1310    // it has the same document reference.
1311  235 if (context.getDoc() != this) {
1312  34 backup = new HashMap<>();
1313  34 backupContext(backup, context);
1314  34 setAsContextDoc(context);
1315    }
1316   
1317    // Make sure to execute the document with the right of the provided sdocument's author
1318  235 if (sDocument != null) {
1319  33 context.put(CKEY_SDOC, sDocument);
1320    }
1321   
1322    // Reuse this document's reference so that the Velocity macro name-space is computed based on it.
1323  235 XWikiDocument fakeDocument = new XWikiDocument(getDocumentReference());
1324  235 fakeDocument.setSyntax(getSyntaxFactory().createSyntaxFromIdString(sourceSyntaxId));
1325  235 fakeDocument.setContent(text);
1326   
1327    // We don't let displayer take care of the context isolation because we don't want the fake document to be
1328    // context document
1329  235 return fakeDocument.display(getSyntaxFactory().createSyntaxFromIdString(targetSyntaxId), false, true,
1330    restrictedTransformationContext, false);
1331    } catch (Exception e) {
1332    // Failed to render for some reason. This method should normally throw an exception but this
1333    // requires changing the signature of calling methods too.
1334  0 LOGGER.warn("Failed to render content [" + text + "]", e);
1335    } finally {
1336  235 if (backup != null) {
1337  34 restoreContext(backup, context);
1338    }
1339  235 context.put(CKEY_SDOC, currentSDocument);
1340   
1341  235 getProgress().endStep(this);
1342    }
1343   
1344  0 return "";
1345    }
1346   
 
1347  0 toggle public String getEscapedContent(XWikiContext context) throws XWikiException
1348    {
1349  0 return XMLUtils.escape(getTranslatedContent(context));
1350    }
1351   
1352    /**
1353    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
1354    *
1355    * @deprecated since 2.2M1 used {@link #getDocumentReference()} instead
1356    */
 
1357  70224 toggle @Deprecated
1358    public String getName()
1359    {
1360  70217 return getDocumentReference().getName();
1361    }
1362   
1363    /**
1364    * Note that this method cannot be removed for now since it's used by Hibernate for loading a XWikiDocument.
1365    *
1366    * @deprecated since 2.2M1 used {@link #setDocumentReference(DocumentReference)} instead
1367    */
 
1368  3644 toggle @Deprecated
1369    public void setName(String name)
1370    {
1371  3643 if (name != null) {
1372  3643 DocumentReference reference = getDocumentReference();
1373    // TODO: ensure that other parameters are copied properly
1374  3644 setDocumentReferenceInternal(
1375    new DocumentReference(name, new SpaceReference(reference.getParent()), reference.getLocale()));
1376    }
1377    }
1378   
 
1379  11749297 toggle @Override
1380    public DocumentReference getDocumentReference()
1381    {
1382  11749302 return this.documentReference;
1383    }
1384   
1385    /**
1386    * @return the {@link DocumentReference} of the document also containing the document {@link Locale}
1387    * @since 5.3M2
1388    */
 
1389  668991 toggle public DocumentReference getDocumentReferenceWithLocale()
1390    {
1391  669007 return new DocumentReference(this.documentReference, getLocale());
1392    }
1393   
1394    /**
1395    * @return the document's space + page name (eg "space.page")
1396    * @deprecated since 2.2M1 use {@link #getDocumentReference()} instead
1397    */
 
1398  155822 toggle @Deprecated
1399    @Override
1400    public String getFullName()
1401    {
1402  155815 return LOCAL_REFERENCE_SERIALIZER.serialize(getDocumentReference());
1403    }
1404   
1405    /**
1406    * @return the docoument's wiki + space + page name (eg "wiki:space.page")
1407    * @deprecated since 2.2M1 use {@link #getDocumentReference()} instead
1408    */
 
1409  48892 toggle @Deprecated
1410    public String getPrefixedFullName()
1411    {
1412  48891 return getDefaultEntityReferenceSerializer().serialize(getDocumentReference());
1413    }
1414   
1415    /**
1416    * @since 2.2M1
1417    * @deprecated since 2.2.3 don't change the reference of a document once it's been constructed. Instead you can
1418    * clone the doc, rename it or copy it.
1419    */
 
1420  1338319 toggle @Deprecated
1421    public void setDocumentReference(DocumentReference reference)
1422    {
1423    // Don't allow setting a null reference for now, ie. don't do anything to preserve backward compatibility
1424    // with previous behavior (i.e. {@link #setFullName}.
1425  1338312 if (reference != null) {
1426    // Retro compatibility, make sure <code>this.documentReference</code> does not contain the Locale (for now)
1427  1338309 DocumentReference referenceWithoutLocale =
1428  1338297 reference.getLocale() != null ? new DocumentReference(reference, null) : reference;
1429   
1430  1338318 if (!referenceWithoutLocale.equals(getDocumentReference())) {
1431  1329586 setDocumentReferenceInternal(referenceWithoutLocale);
1432    }
1433    }
1434    }
1435   
 
1436  1340455 toggle private void setDocumentReferenceInternal(DocumentReference reference)
1437    {
1438  1340487 this.documentReference = reference;
1439   
1440  1340477 setMetaDataDirty(true);
1441   
1442    // Clean various caches
1443   
1444  1340495 this.keyCache = null;
1445  1340488 this.localKeyCache = null;
1446  1340499 this.parentReferenceCache = null;
1447    }
1448   
1449    /**
1450    * @deprecated since 2.2M1 use {@link #setDocumentReference(org.xwiki.model.reference.DocumentReference)} instead
1451    */
 
1452  3712 toggle @Deprecated
1453    public void setFullName(String name)
1454    {
1455  3712 setFullName(name, null);
1456    }
1457   
1458    /**
1459    * @deprecated since 2.2M1 use {@link #setDocumentReference(org.xwiki.model.reference.DocumentReference)} instead
1460    */
 
1461  23387 toggle @Deprecated
1462    public void setFullName(String fullName, XWikiContext context)
1463    {
1464    // We ignore the passed full name if it's null to be backward compatible with previous behaviors and to be
1465    // consistent with {@link #setName} and {@link #setSpace}.
1466  23400 if (fullName != null) {
1467    // Note: We use the CurrentMixed Resolver since we want to use the default page name if the page isn't
1468    // specified in the passed string, rather than use the current document's page name.
1469  23405 setDocumentReference(getCurrentMixedDocumentReferenceResolver().resolve(fullName));
1470    }
1471    }
1472   
1473    /**
1474    * {@inheritDoc}
1475    *
1476    * @see DocumentModelBridge#getWikiName()
1477    * @deprecated since 2.2M1 use {@link #getDocumentReference()} instead
1478    */
 
1479  464 toggle @Deprecated
1480    @Override
1481    public String getWikiName()
1482    {
1483  464 return getDatabase();
1484    }
1485   
1486    /**
1487    * {@inheritDoc}
1488    *
1489    * @see DocumentModelBridge#getSpaceName()
1490    * @deprecated since 2.2M1 use {@link #getDocumentReference()} instead
1491    */
 
1492  7 toggle @Deprecated
1493    @Override
1494    public String getSpaceName()
1495    {
1496  7 return this.getSpace();
1497    }
1498   
1499    /**
1500    * {@inheritDoc}
1501    *
1502    * @see DocumentModelBridge#getSpaceName()
1503    * @deprecated since 2.2M1 use {@link #getDocumentReference()} instead
1504    */
 
1505  8 toggle @Deprecated
1506    @Override
1507    public String getPageName()
1508    {
1509  8 return this.getName();
1510    }
1511   
 
1512  70435 toggle @Override
1513    public String getTitle()
1514    {
1515  70430 return (this.title != null) ? this.title : "";
1516    }
1517   
1518    /**
1519    * Get the rendered version of the document title. The title is extracted and then Velocity is applied on it and
1520    * it's then rendered using the passed Syntax. The following logic is used to extract the title:
1521    * <ul>
1522    * <li>If a Sheet is specified for the document and this Sheet document contains a non empty title then it's
1523    * used</li>
1524    * <li>If not and the document's title is specified then it's used</li>
1525    * <li>If not and if the title compatibility mode is turned on ({@code xwiki.title.compatibility=1} in
1526    * {@code xwiki.cfg}) then an attempt is made to extract the title from the first heading found in the document's
1527    * content</li>
1528    * <li>If not, then at last resort the page name is returned</li>
1529    * </ul>
1530    *
1531    * @param outputSyntax the syntax to render to; this is not taken into account for XWiki 1.0 syntax
1532    * @param context the XWiki context
1533    * @return the rendered version of the document title
1534    */
 
1535  19706 toggle public String getRenderedTitle(Syntax outputSyntax, XWikiContext context)
1536    {
1537  19707 DocumentDisplayerParameters parameters = new DocumentDisplayerParameters();
1538  19705 parameters.setTitleDisplayed(true);
1539  19705 parameters.setExecutionContextIsolated(true);
1540  19706 parameters.setTargetSyntax(outputSyntax);
1541  19705 try {
1542  19706 XDOM titleXDOM = getDocumentDisplayer().display(this, parameters);
1543  19706 return renderXDOM(titleXDOM, outputSyntax);
1544    } catch (Exception e) {
1545    // We've failed to extract the Document's title or to render it. We log an error but we use the page name
1546    // as the returned title in order to not generate errors in lots of places in the wiki (e.g. Activity
1547    // Stream, menus, etc). The title is used in a lots of places...
1548  0 LOGGER.error("Failed to render title for [{}]", getDocumentReference(), e);
1549  0 return getDocumentReference().getName();
1550    }
1551    }
1552   
1553    /**
1554    * Similar to {@link #getRenderedTitle(Syntax, XWikiContext)} but the output Syntax used is XHTML 1.0 unless the
1555    * current skin defines another output Syntax in which case it's the one used.
1556    *
1557    * @param context the XWiki context
1558    * @return the rendered version of the document title
1559    */
 
1560  15959 toggle public String getRenderedTitle(XWikiContext context)
1561    {
1562  15958 return getRenderedTitle(getOutputSyntax(), context);
1563    }
1564   
 
1565  43952 toggle public void setTitle(String title)
1566    {
1567  43949 if (title != null && !title.equals(this.title)) {
1568    // Document titles usually contain velocity script, so it is not enough to set the metadata dirty, since we
1569    // want to content author to be updated for programming or script rights to be updated.
1570  38639 setContentDirty(true);
1571    }
1572  43948 this.title = title;
1573    }
1574   
 
1575  31541 toggle public String getFormat()
1576    {
1577  31540 return this.format != null ? this.format : "";
1578    }
1579   
 
1580  31328 toggle public void setFormat(String format)
1581    {
1582  31326 this.format = format;
1583  31327 if (!format.equals(this.format)) {
1584  0 setMetaDataDirty(true);
1585    }
1586    }
1587   
1588    /**
1589    * @param userString the user {@link String} to convert to {@link DocumentReference}
1590    * @return the user as {@link DocumentReference}
1591    */
 
1592  12256 toggle private DocumentReference userStringToReference(String userString)
1593    {
1594  12258 DocumentReference userReference;
1595   
1596  12260 if (StringUtils.isEmpty(userString)) {
1597  0 userReference = null;
1598    } else {
1599  12260 userReference = getExplicitReferenceDocumentReferenceResolver().resolve(
1600    getXClassEntityReferenceResolver().resolve(userString, EntityType.DOCUMENT), getDocumentReference());
1601   
1602  12260 if (userReference.getName().equals(XWikiRightService.GUEST_USER)) {
1603  95 userReference = null;
1604    }
1605    }
1606   
1607  12260 return userReference;
1608    }
1609   
1610    /**
1611    * @param userReference the user {@link DocumentReference} to convert to {@link String}
1612    * @return the user as String
1613    */
 
1614  51257 toggle private String userReferenceToString(DocumentReference userReference)
1615    {
1616  51257 String userString;
1617   
1618  51257 if (userReference != null) {
1619  50648 userString = getCompactWikiEntityReferenceSerializer().serialize(userReference, getDocumentReference());
1620    } else {
1621  609 userString = XWikiRightService.GUEST_USER_FULLNAME;
1622    }
1623   
1624  51257 return userString;
1625    }
1626   
1627    /**
1628    * @since 3.0M3
1629    */
 
1630  191543 toggle public DocumentReference getAuthorReference()
1631    {
1632  191542 return this.authorReference;
1633    }
1634   
1635    /**
1636    * @since 3.0M3
1637    */
 
1638  129604 toggle public void setAuthorReference(DocumentReference authorReference)
1639    {
1640  129599 if (ObjectUtils.notEqual(authorReference, getAuthorReference())) {
1641  112592 setMetaDataDirty(true);
1642    }
1643   
1644  129596 this.authorReference = authorReference;
1645   
1646    // Log this since it's probably a mistake so that we find who is doing bad things
1647  129607 if (this.authorReference != null && this.authorReference.getName().equals(XWikiRightService.GUEST_USER)) {
1648  0 LOGGER.warn("A reference to XWikiGuest user has been set instead of null. This is probably a mistake.",
1649    new Exception("See stack trace"));
1650    }
1651    }
1652   
1653    /**
1654    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
1655    *
1656    * @deprecated since 3.0M3 use {@link #getAuthorReference()} instead
1657    */
 
1658  21306 toggle @Deprecated
1659    public String getAuthor()
1660    {
1661  21306 return userReferenceToString(getAuthorReference());
1662    }
1663   
1664    /**
1665    * Note that this method cannot be removed for now since it's used by Hibernate for loading a XWikiDocument.
1666    *
1667    * @deprecated since 3.0M3 use {@link #setAuthorReference} instead
1668    */
 
1669  3875 toggle @Deprecated
1670    public void setAuthor(String author)
1671    {
1672  3876 setAuthorReference(userStringToReference(author));
1673    }
1674   
1675    /**
1676    * @since 3.0M3
1677    */
 
1678  201045 toggle @Override
1679    public DocumentReference getContentAuthorReference()
1680    {
1681  201050 return this.contentAuthorReference;
1682    }
1683   
1684    /**
1685    * @since 3.0M3
1686    */
 
1687  129303 toggle public void setContentAuthorReference(DocumentReference contentAuthorReference)
1688    {
1689  129308 if (ObjectUtils.notEqual(contentAuthorReference, getContentAuthorReference())) {
1690  112541 setMetaDataDirty(true);
1691    }
1692   
1693  129287 this.contentAuthorReference = contentAuthorReference;
1694   
1695    // Log this since it's probably a mistake so that we find who is doing bad things
1696  129305 if (this.contentAuthorReference != null
1697    && this.contentAuthorReference.getName().equals(XWikiRightService.GUEST_USER)) {
1698  0 LOGGER.warn("A reference to XWikiGuest user has been set instead of null. This is probably a mistake.",
1699    new Exception("See stack trace"));
1700    }
1701    }
1702   
1703    /**
1704    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
1705    *
1706    * @deprecated since 3.0M3 use {@link #getContentAuthorReference()} instead
1707    */
 
1708  14810 toggle @Deprecated
1709    public String getContentAuthor()
1710    {
1711  14810 return userReferenceToString(getContentAuthorReference());
1712    }
1713   
1714    /**
1715    * Note that this method cannot be removed for now since it's used by Hibernate for loading a XWikiDocument.
1716    *
1717    * @deprecated since 3.0M3 use {@link #setContentAuthorReference} instead
1718    */
 
1719  3644 toggle @Deprecated
1720    public void setContentAuthor(String contentAuthor)
1721    {
1722  3643 setContentAuthorReference(userStringToReference(contentAuthor));
1723    }
1724   
1725    /**
1726    * @since 3.0M3
1727    */
 
1728  178823 toggle public DocumentReference getCreatorReference()
1729    {
1730  178833 return this.creatorReference;
1731    }
1732   
1733    /**
1734    * @since 3.0M3
1735    */
 
1736  126431 toggle public void setCreatorReference(DocumentReference creatorReference)
1737    {
1738  126434 if (ObjectUtils.notEqual(creatorReference, getCreatorReference())) {
1739  110414 setMetaDataDirty(true);
1740    }
1741   
1742  126433 this.creatorReference = creatorReference;
1743   
1744    // Log this since it's probably a mistake so that we find who is doing bad things
1745  126437 if (this.creatorReference != null && this.creatorReference.getName().equals(XWikiRightService.GUEST_USER)) {
1746  0 LOGGER.warn("A reference to XWikiGuest user has been set instead of null. This is probably a mistake.",
1747    new Exception("See stack trace"));
1748    }
1749    }
1750   
1751    /**
1752    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
1753    *
1754    * @deprecated since 3.0M2 use {@link #getCreatorReference()} instead
1755    */
 
1756  15141 toggle @Deprecated
1757    public String getCreator()
1758    {
1759  15141 return userReferenceToString(getCreatorReference());
1760    }
1761   
1762    /**
1763    * Note that this method cannot be removed for now since it's used by Hibernate for loading a XWikiDocument.
1764    *
1765    * @deprecated since 3.0M2 use {@link #setCreatorReference} instead
1766    */
 
1767  4739 toggle @Deprecated
1768    public void setCreator(String creator)
1769    {
1770  4740 setCreatorReference(userStringToReference(creator));
1771    }
1772   
 
1773  56626 toggle public Date getDate()
1774    {
1775  56629 if (this.updateDate == null) {
1776  0 return new Date();
1777    } else {
1778  56631 return this.updateDate;
1779    }
1780    }
1781   
 
1782  50372 toggle public void setDate(Date date)
1783    {
1784  50380 if ((date != null) && (!date.equals(this.updateDate))) {
1785  31667 setMetaDataDirty(true);
1786    }
1787    // Make sure we drop milliseconds for consistency with the database
1788  50377 if (date != null) {
1789  50376 date.setTime((date.getTime() / 1000) * 1000);
1790    }
1791  50376 this.updateDate = date;
1792    }
1793   
 
1794  51027 toggle public Date getCreationDate()
1795    {
1796  51031 if (this.creationDate == null) {
1797  23 return new Date();
1798    } else {
1799  51008 return this.creationDate;
1800    }
1801    }
1802   
 
1803  46207 toggle public void setCreationDate(Date date)
1804    {
1805  46216 if ((date != null) && (!date.equals(this.creationDate))) {
1806  25207 setMetaDataDirty(true);
1807    }
1808   
1809    // Make sure we drop milliseconds for consistency with the database
1810  46213 if (date != null) {
1811  43606 date.setTime((date.getTime() / 1000) * 1000);
1812    }
1813  46212 this.creationDate = date;
1814    }
1815   
 
1816  50574 toggle public Date getContentUpdateDate()
1817    {
1818  50575 if (this.contentUpdateDate == null) {
1819  0 return new Date();
1820    } else {
1821  50574 return this.contentUpdateDate;
1822    }
1823    }
1824   
 
1825  50008 toggle public void setContentUpdateDate(Date date)
1826    {
1827  50009 if ((date != null) && (!date.equals(this.contentUpdateDate))) {
1828  30482 setMetaDataDirty(true);
1829    }
1830   
1831    // Make sure we drop milliseconds for consistency with the database
1832  50013 if (date != null) {
1833  50010 date.setTime((date.getTime() / 1000) * 1000);
1834    }
1835  50011 this.contentUpdateDate = date;
1836    }
1837   
 
1838  31323 toggle public String getMeta()
1839    {
1840  31327 return this.meta;
1841    }
1842   
 
1843  31326 toggle public void setMeta(String meta)
1844    {
1845  31324 if (meta == null) {
1846  31328 if (this.meta != null) {
1847  0 setMetaDataDirty(true);
1848    }
1849  0 } else if (!meta.equals(this.meta)) {
1850  0 setMetaDataDirty(true);
1851    }
1852  31328 this.meta = meta;
1853    }
1854   
 
1855  0 toggle public void appendMeta(String meta)
1856    {
1857  0 StringBuilder buf = new StringBuilder(this.meta);
1858  0 buf.append(meta);
1859  0 buf.append("\n");
1860  0 this.meta = buf.toString();
1861  0 setMetaDataDirty(true);
1862    }
1863   
 
1864  41233 toggle public boolean isContentDirty()
1865    {
1866  41229 return this.isContentDirty;
1867    }
1868   
 
1869  2200 toggle public void incrementVersion()
1870    {
1871  2200 if (this.version == null) {
1872  1763 this.version = new Version("1.1");
1873    } else {
1874  437 if (isMinorEdit()) {
1875  72 this.version = this.version.next();
1876    } else {
1877  365 this.version = this.version.getBranchPoint().next().newBranch(1);
1878    }
1879    }
1880    }
1881   
 
1882  658476 toggle public void setContentDirty(boolean contentDirty)
1883    {
1884  658475 this.isContentDirty = contentDirty;
1885    }
1886   
 
1887  37223 toggle public boolean isMetaDataDirty()
1888    {
1889  37225 return this.isMetaDataDirty;
1890    }
1891   
 
1892  5978428 toggle public void setMetaDataDirty(boolean metaDataDirty)
1893    {
1894  5978037 this.isMetaDataDirty = metaDataDirty;
1895    }
1896   
 
1897  9 toggle public String getAttachmentURL(String filename, XWikiContext context)
1898    {
1899  9 return getAttachmentURL(filename, "download", context);
1900    }
1901   
 
1902  12 toggle public String getAttachmentURL(String filename, String action, XWikiContext context)
1903    {
1904  12 return getAttachmentURL(filename, action, null, context);
1905    }
1906   
 
1907  0 toggle public String getExternalAttachmentURL(String filename, String action, XWikiContext context)
1908    {
1909  0 URL url = context.getURLFactory().createAttachmentURL(filename, getSpace(), getName(), action, null,
1910    getDatabase(), context);
1911  0 return url.toString();
1912    }
1913   
 
1914  36 toggle public String getAttachmentURL(String filename, String action, String querystring, XWikiContext context)
1915    {
1916    // Attachment file name cannot be empty
1917  36 if (StringUtils.isEmpty(filename)) {
1918  0 return null;
1919    }
1920   
1921  36 return context.getWiki().getAttachmentURL(new AttachmentReference(filename, this.getDocumentReference()),
1922    action, querystring, context);
1923    }
1924   
 
1925  0 toggle public String getAttachmentRevisionURL(String filename, String revision, XWikiContext context)
1926    {
1927  0 return getAttachmentRevisionURL(filename, revision, null, context);
1928    }
1929   
 
1930  0 toggle public String getAttachmentRevisionURL(String filename, String revision, String querystring, XWikiContext context)
1931    {
1932    // Attachment file name cannot be empty
1933  0 if (StringUtils.isEmpty(filename)) {
1934  0 return null;
1935    }
1936   
1937  0 return context.getWiki().getAttachmentRevisionURL(new AttachmentReference(filename, getDocumentReference()),
1938    revision, querystring, context);
1939    }
1940   
1941    /**
1942    * @param action the action, see the {@code struts-config.xml} file for a list of all existing action names
1943    * @param params the URL query string
1944    * @param redirect true if the URL is going to be used in {@link HttpServletResponse#sendRedirect(String)}
1945    * @param context the XWiki context
1946    * @return the URL
1947    */
 
1948  34776 toggle public String getURL(String action, String params, boolean redirect, XWikiContext context)
1949    {
1950  34777 URL url =
1951    context.getURLFactory().createURL(getSpace(), getName(), action, params, null, getDatabase(), context);
1952   
1953  34778 if (redirect && isRedirectAbsolute(context)) {
1954  0 if (url == null) {
1955  0 return null;
1956    } else {
1957  0 return url.toString();
1958    }
1959    } else {
1960  34775 return context.getURLFactory().getURL(url, context);
1961    }
1962    }
1963   
 
1964  163 toggle private boolean isRedirectAbsolute(XWikiContext context)
1965    {
1966  163 return StringUtils.equals("1", context.getWiki().Param("xwiki.redirect.absoluteurl"));
1967    }
1968   
 
1969  34613 toggle public String getURL(String action, boolean redirect, XWikiContext context)
1970    {
1971  34614 return getURL(action, null, redirect, context);
1972    }
1973   
 
1974  34616 toggle public String getURL(String action, XWikiContext context)
1975    {
1976  34616 return getURL(action, false, context);
1977    }
1978   
 
1979  13537 toggle public String getURL(String action, String querystring, XWikiContext context)
1980    {
1981  13538 URL url =
1982    context.getURLFactory().createURL(getSpace(), getName(), action, querystring, null, getDatabase(), context);
1983  13538 return context.getURLFactory().getURL(url, context);
1984    }
1985   
 
1986  0 toggle public String getURL(String action, String querystring, String anchor, XWikiContext context)
1987    {
1988  0 URL url = context.getURLFactory().createURL(getSpace(), getName(), action, querystring, anchor, getDatabase(),
1989    context);
1990  0 return context.getURLFactory().getURL(url, context);
1991    }
1992   
 
1993  640 toggle public String getExternalURL(String action, XWikiContext context)
1994    {
1995  640 URL url = context.getURLFactory().createExternalURL(getSpace(), getName(), action, null, null, getDatabase(),
1996    context);
1997  640 return url.toString();
1998    }
1999   
 
2000  25 toggle public String getExternalURL(String action, String querystring, XWikiContext context)
2001    {
2002  25 URL url = context.getURLFactory().createExternalURL(getSpace(), getName(), action, querystring, null,
2003    getDatabase(), context);
2004  25 return url.toString();
2005    }
2006   
 
2007  0 toggle public String getParentURL(XWikiContext context) throws XWikiException
2008    {
2009  0 XWikiDocument doc = new XWikiDocument(getParentReference());
2010  0 URL url = context.getURLFactory().createURL(doc.getSpace(), doc.getName(), "view", null, null, getDatabase(),
2011    context);
2012  0 return context.getURLFactory().getURL(url, context);
2013    }
2014   
 
2015  2221 toggle public XWikiDocumentArchive getDocumentArchive(XWikiContext context) throws XWikiException
2016    {
2017  2221 loadArchive(context);
2018  2221 return getDocumentArchive();
2019    }
2020   
2021    /**
2022    * Create a new protected {@link com.xpn.xwiki.api.Document} public API to access page information and actions from
2023    * scripting.
2024    *
2025    * @param customClassName the name of the custom {@link com.xpn.xwiki.api.Document} class of the object to create.
2026    * @param context the XWiki context.
2027    * @return a wrapped version of an XWikiDocument. Prefer this function instead of new Document(XWikiDocument,
2028    * XWikiContext)
2029    */
 
2030  193316 toggle public com.xpn.xwiki.api.Document newDocument(String customClassName, XWikiContext context)
2031    {
2032  193310 if (!((customClassName == null) || (customClassName.equals("")))) {
2033  0 try {
2034  0 return newDocument(Class.forName(customClassName), context);
2035    } catch (ClassNotFoundException e) {
2036  0 LOGGER.error("Failed to get java Class object from class name", e);
2037    }
2038    }
2039   
2040  193289 return new com.xpn.xwiki.api.Document(this, context);
2041    }
2042   
2043    /**
2044    * Create a new protected {@link com.xpn.xwiki.api.Document} public API to access page information and actions from
2045    * scripting.
2046    *
2047    * @param customClass the custom {@link com.xpn.xwiki.api.Document} class the object to create.
2048    * @param context the XWiki context.
2049    * @return a wrapped version of an XWikiDocument. Prefer this function instead of new Document(XWikiDocument,
2050    * XWikiContext)
2051    */
 
2052  0 toggle public com.xpn.xwiki.api.Document newDocument(Class<?> customClass, XWikiContext context)
2053    {
2054  0 if (customClass != null) {
2055  0 try {
2056  0 Class<?>[] classes = new Class[] { XWikiDocument.class, XWikiContext.class };
2057  0 Object[] args = new Object[] { this, context };
2058   
2059  0 return (com.xpn.xwiki.api.Document) customClass.getConstructor(classes).newInstance(args);
2060    } catch (Exception e) {
2061  0 LOGGER.error("Failed to create a custom Document object", e);
2062    }
2063    }
2064   
2065  0 return new com.xpn.xwiki.api.Document(this, context);
2066    }
2067   
 
2068  193314 toggle public com.xpn.xwiki.api.Document newDocument(XWikiContext context)
2069    {
2070  193307 String customClass = getCustomClass();
2071  193311 return newDocument(customClass, context);
2072    }
2073   
 
2074  2347 toggle public void loadArchive(XWikiContext context) throws XWikiException
2075    {
2076  2347 if (this.archive == null || this.archive.get() == null) {
2077  2304 XWikiDocumentArchive arch = getVersioningStore(context).getXWikiDocumentArchive(this, context);
2078    // We are using a SoftReference which will allow the archive to be
2079    // discarded by the Garbage collector as long as the context is closed (usually during
2080    // the request)
2081  2304 this.archive = new SoftReference<XWikiDocumentArchive>(arch);
2082    }
2083    }
2084   
2085    /**
2086    * @return the {@link XWikiDocumentArchive} for this document. If it is not stored in the document, null is
2087    * returned.
2088    */
 
2089  50129 toggle public XWikiDocumentArchive getDocumentArchive()
2090    {
2091    // If there is a soft reference, return it.
2092  50130 if (this.archive != null) {
2093  15145 return this.archive.get();
2094    }
2095  34981 return null;
2096    }
2097   
2098    /**
2099    * @return the {@link XWikiDocumentArchive} for this document. If it is not stored in the document, we get it using
2100    * the current context. If there is an exception, null is returned.
2101    */
 
2102  7 toggle public XWikiDocumentArchive loadDocumentArchive()
2103    {
2104  7 XWikiDocumentArchive arch = getDocumentArchive();
2105  7 if (arch != null) {
2106  6 return arch;
2107    }
2108   
2109  1 XWikiContext xcontext = getXWikiContext();
2110   
2111  1 try {
2112  1 arch = getVersioningStore(xcontext).getXWikiDocumentArchive(this, xcontext);
2113   
2114    // Put a copy of the archive in the soft reference for later use if needed.
2115  1 setDocumentArchive(arch);
2116   
2117  1 return arch;
2118    } catch (Exception e) {
2119    // VersioningStore.getXWikiDocumentArchive may throw an XWikiException, and xcontext or VersioningStore
2120    // may be null (tests)
2121    // To maintain the behavior of this method we can't throw an exception.
2122    // Formerly, null was returned if there was no SoftReference.
2123  0 LOGGER.warn("Could not get document archive", e);
2124  0 return null;
2125    }
2126    }
2127   
 
2128  35417 toggle public void setDocumentArchive(XWikiDocumentArchive arch)
2129    {
2130    // We are using a SoftReference which will allow the archive to be
2131    // discarded by the Garbage collector as long as the context is closed (usually during the
2132    // request)
2133  35417 if (arch != null) {
2134  8412 this.archive = new SoftReference<XWikiDocumentArchive>(arch);
2135    }
2136    }
2137   
 
2138  8 toggle public void setDocumentArchive(String sarch) throws XWikiException
2139    {
2140  8 XWikiDocumentArchive xda = new XWikiDocumentArchive(getId());
2141  8 xda.setArchive(sarch);
2142  8 setDocumentArchive(xda);
2143    }
2144   
 
2145  2127 toggle public Version[] getRevisions(XWikiContext context) throws XWikiException
2146    {
2147  2127 return getVersioningStore(context).getXWikiDocVersions(this, context);
2148    }
2149   
 
2150  0 toggle public String[] getRecentRevisions(int nb, XWikiContext context) throws XWikiException
2151    {
2152  0 try {
2153  0 Version[] revisions = getVersioningStore(context).getXWikiDocVersions(this, context);
2154  0 int length = nb;
2155    // 0 means all revisions
2156  0 if (nb == 0) {
2157  0 length = revisions.length;
2158    }
2159   
2160  0 if (revisions.length < length) {
2161  0 length = revisions.length;
2162    }
2163   
2164  0 String[] recentrevs = new String[length];
2165  0 for (int i = 1; i <= length; i++) {
2166  0 recentrevs[i - 1] = revisions[revisions.length - i].toString();
2167    }
2168  0 return recentrevs;
2169    } catch (Exception e) {
2170  0 return new String[0];
2171    }
2172    }
2173   
2174    /**
2175    * Get document versions matching criterias like author, minimum creation date, etc.
2176    *
2177    * @param criteria criteria used to match versions
2178    * @return a list of matching versions
2179    */
 
2180  14 toggle public List<String> getRevisions(RevisionCriteria criteria, XWikiContext context) throws XWikiException
2181    {
2182  14 List<String> results = new ArrayList<String>();
2183   
2184  14 Version[] revisions = getRevisions(context);
2185   
2186  14 XWikiRCSNodeInfo nextNodeinfo = null;
2187  14 XWikiRCSNodeInfo nodeinfo;
2188  14 for (Version revision : revisions) {
2189  22 nodeinfo = nextNodeinfo;
2190  22 nextNodeinfo = getRevisionInfo(revision.toString(), context);
2191   
2192  22 if (nodeinfo == null) {
2193  14 continue;
2194    }
2195   
2196    // Minor/Major version matching
2197  8 if (criteria.getIncludeMinorVersions() || !nextNodeinfo.isMinorEdit()) {
2198    // Author matching
2199  8 if (criteria.getAuthor().equals("") || criteria.getAuthor().equals(nodeinfo.getAuthor())) {
2200    // Date range matching
2201  8 Date versionDate = nodeinfo.getDate();
2202  8 if (versionDate.after(criteria.getMinDate()) && versionDate.before(criteria.getMaxDate())) {
2203  8 results.add(nodeinfo.getVersion().toString());
2204    }
2205    }
2206    }
2207    }
2208   
2209  14 nodeinfo = nextNodeinfo;
2210  14 if (nodeinfo != null) {
2211  14 if (criteria.getAuthor().equals("") || criteria.getAuthor().equals(nodeinfo.getAuthor())) {
2212    // Date range matching
2213  14 Date versionDate = nodeinfo.getDate();
2214  14 if (versionDate.after(criteria.getMinDate()) && versionDate.before(criteria.getMaxDate())) {
2215  14 results.add(nodeinfo.getVersion().toString());
2216    }
2217    }
2218    }
2219   
2220  14 return criteria.getRange().subList(results);
2221    }
2222   
 
2223  33 toggle public XWikiRCSNodeInfo getRevisionInfo(String version, XWikiContext context) throws XWikiException
2224    {
2225  33 return getDocumentArchive(context).getNode(new Version(version));
2226    }
2227   
2228    /**
2229    * @return Is this version the most recent one. False if and only if there are newer versions of this document in
2230    * the database.
2231    */
 
2232  31329 toggle public boolean isMostRecent()
2233    {
2234  31331 return this.mostRecent;
2235    }
2236   
2237    /**
2238    * must not be used unless in store system.
2239    *
2240    * @param mostRecent - mark document as most recent.
2241    */
 
2242  34969 toggle public void setMostRecent(boolean mostRecent)
2243    {
2244  34976 this.mostRecent = mostRecent;
2245    }
2246   
2247    /**
2248    * @since 2.2M1
2249    */
 
2250  73220 toggle public BaseClass getXClass()
2251    {
2252  73220 if (this.xClass == null) {
2253  15094 BaseClass emptyClass = new BaseClass();
2254    // Make sure not to cause any false document versions if this document is saved.
2255  15094 emptyClass.setDirty(false);
2256   
2257  15094 this.setXClass(emptyClass);
2258    }
2259  73220 return this.xClass;
2260    }
2261   
2262    /**
2263    * @since 2.2M1
2264    */
 
2265  39434 toggle public void setXClass(BaseClass xwikiClass)
2266    {
2267  39435 xwikiClass.setOwnerDocument(this);
2268   
2269  39432 this.xClass = xwikiClass;
2270    }
2271   
2272    /**
2273    * @since 2.2M1
2274    */
 
2275  285444 toggle public Map<DocumentReference, List<BaseObject>> getXObjects()
2276    {
2277  285437 return this.xObjects;
2278    }
2279   
2280    /**
2281    * @since 2.2M1
2282    */
 
2283  34 toggle public void setXObjects(Map<DocumentReference, List<BaseObject>> objects)
2284    {
2285  34 if (objects == null) {
2286    // Make sure we don`t set a null objects map since we assume everywhere that it is not null when using it.
2287  0 objects = new HashMap<>();
2288    }
2289   
2290  34 boolean isDirty = false;
2291   
2292  34 for (List<BaseObject> objList : objects.values()) {
2293  0 for (BaseObject obj : objList) {
2294  0 obj.setOwnerDocument(this);
2295  0 isDirty = true;
2296    }
2297    }
2298   
2299    // This operation resulted in marking the current document dirty.
2300  34 if (isDirty) {
2301  0 setMetaDataDirty(true);
2302    }
2303   
2304    // Replace the current objects with the provided ones.
2305  34 this.xObjects = objects;
2306    }
2307   
2308    /**
2309    * @since 2.2M1
2310    */
 
2311  65 toggle public BaseObject getXObject()
2312    {
2313  65 return getXObject(getDocumentReference());
2314    }
2315   
2316    /**
2317    * @deprecated since 2.2M1 use {@link #getXObject()} instead
2318    */
 
2319  0 toggle @Deprecated
2320    public BaseObject getxWikiObject()
2321    {
2322  0 return getXObject(getDocumentReference());
2323    }
2324   
2325    /**
2326    * @since 2.2M1
2327    */
 
2328  44 toggle public List<BaseClass> getXClasses(XWikiContext context)
2329    {
2330  44 List<BaseClass> list = new ArrayList<BaseClass>();
2331   
2332    // getXObjects() is a TreeMap, with elements sorted by className reference
2333  44 for (DocumentReference classReference : getXObjects().keySet()) {
2334  68 BaseClass bclass = null;
2335  68 List<BaseObject> objects = getXObjects(classReference);
2336  68 for (BaseObject obj : objects) {
2337  68 if (obj != null) {
2338  68 bclass = obj.getXClass(context);
2339  68 if (bclass != null) {
2340  68 break;
2341    }
2342    }
2343    }
2344  68 if (bclass != null) {
2345  68 list.add(bclass);
2346    }
2347    }
2348  44 return list;
2349    }
2350   
2351    /**
2352    * Create and add a new object to the document with the provided class.
2353    * <p>
2354    * Note that absolute reference are not supported for xclasses which mean that the wiki part (whatever the wiki is)
2355    * of the reference will be systematically removed.
2356    *
2357    * @param classReference the reference of the class
2358    * @param context the XWiki context
2359    * @return the index of teh newly created object
2360    * @throws XWikiException error when creating the new object
2361    * @since 2.2.3
2362    */
 
2363  1546 toggle public int createXObject(EntityReference classReference, XWikiContext context) throws XWikiException
2364    {
2365  1546 DocumentReference absoluteClassReference = resolveClassReference(classReference);
2366  1546 BaseObject object = BaseClass.newCustomClassInstance(absoluteClassReference, context);
2367  1546 object.setOwnerDocument(this);
2368  1546 object.setXClassReference(classReference);
2369  1546 List<BaseObject> objects = this.xObjects.get(absoluteClassReference);
2370  1546 if (objects == null) {
2371  1470 objects = new ArrayList<BaseObject>();
2372  1470 this.xObjects.put(absoluteClassReference, objects);
2373    }
2374  1546 objects.add(object);
2375  1546 int nb = objects.size() - 1;
2376  1546 object.setNumber(nb);
2377  1546 setMetaDataDirty(true);
2378  1546 return nb;
2379    }
2380   
2381    /**
2382    * @deprecated since 2.2M1 use {@link #createXObject(EntityReference, XWikiContext)} instead
2383    */
 
2384  89 toggle @Deprecated
2385    public int createNewObject(String className, XWikiContext context) throws XWikiException
2386    {
2387  89 return createXObject(
2388    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()),
2389    context);
2390    }
2391   
2392    /**
2393    * @since 2.2M1
2394    */
 
2395  508 toggle public int getXObjectSize(DocumentReference classReference)
2396    {
2397  508 try {
2398  508 return getXObjects().get(classReference).size();
2399    } catch (Exception e) {
2400  479 return 0;
2401    }
2402    }
2403   
2404    /**
2405    * @since 7.3M1
2406    * @since 7.2.1
2407    * @since 7.1.3
2408    * @since 6.4.6
2409    */
 
2410  0 toggle public int getXObjectSize(EntityReference classReference)
2411    {
2412  0 return getXObjectSize(resolveClassReference(classReference));
2413    }
2414   
2415    /**
2416    * @deprecated since 2.2M1 use {@link #getXObjectSize(DocumentReference)} instead
2417    */
 
2418  0 toggle @Deprecated
2419    public int getObjectNumbers(String className)
2420    {
2421  0 return getXObjectSize(resolveClassReference(className));
2422    }
2423   
2424    /**
2425    * @since 2.2M1
2426    */
 
2427  64744 toggle public List<BaseObject> getXObjects(DocumentReference classReference)
2428    {
2429  64743 if (classReference == null) {
2430  0 return new ArrayList<BaseObject>();
2431    }
2432   
2433  64741 return getXObjects().get(classReference);
2434    }
2435   
2436    /**
2437    * @since 3.3M1
2438    */
 
2439  19766 toggle public List<BaseObject> getXObjects(EntityReference reference)
2440    {
2441  19766 if (reference.getType() == EntityType.DOCUMENT) {
2442    // class reference
2443  19765 return getXObjects(
2444    getCurrentReferenceDocumentReferenceResolver().resolve(reference, getDocumentReference()));
2445    }
2446   
2447  0 return Collections.emptyList();
2448    }
2449   
2450    /**
2451    * @deprecated since 2.2M1 use {@link #getXObjects(DocumentReference)} instead
2452    */
 
2453  15497 toggle @Deprecated
2454    public Vector<BaseObject> getObjects(String className)
2455    {
2456  15495 List<BaseObject> result = this.xObjects.get(resolveClassReference(className));
2457  15496 return result == null ? null : new Vector<BaseObject>(result);
2458    }
2459   
2460    /**
2461    * @since 2.2M1
2462    */
 
2463  1 toggle public void setXObjects(DocumentReference classReference, List<BaseObject> objects)
2464    {
2465    // Remove existing objects
2466  1 List<BaseObject> existingbjects = this.xObjects.get(classReference);
2467  1 if (existingbjects != null) {
2468  1 existingbjects.clear();
2469    }
2470   
2471  1 for (BaseObject obj : objects) {
2472  1 obj.setOwnerDocument(this);
2473    }
2474   
2475    // Add new objects
2476  1 if (objects.isEmpty()) {
2477    // Pretty wrong but can't remove that for retro compatibility reasons...
2478    // Note that it means that someone can put an unmodifiable list here make impossible to add any object of
2479    // this class.
2480  0 this.xObjects.put(classReference, objects);
2481    } else {
2482  1 for (BaseObject baseObject : objects) {
2483  1 addXObject(classReference, baseObject);
2484    }
2485    }
2486   
2487  1 setMetaDataDirty(true);
2488    }
2489   
2490    /**
2491    * @since 3.3M1
2492    */
 
2493  50992 toggle public BaseObject getXObject(EntityReference reference)
2494    {
2495  50995 if (reference instanceof DocumentReference) {
2496  0 return getXObject((DocumentReference) reference);
2497  50993 } else if (reference.getType() == EntityType.DOCUMENT) {
2498    // class reference
2499  44267 return getXObject(
2500    getCurrentReferenceDocumentReferenceResolver().resolve(reference, getDocumentReference()));
2501  6715 } else if (reference.getType() == EntityType.OBJECT) {
2502    // object reference
2503  6717 return getXObject(getCurrentReferenceObjectReferenceResolver().resolve(reference, getDocumentReference()));
2504    }
2505   
2506  0 return null;
2507    }
2508   
2509    /**
2510    * @since 2.2M1
2511    */
 
2512  107022 toggle public BaseObject getXObject(DocumentReference classReference)
2513    {
2514  107021 BaseObject result = null;
2515  107009 List<BaseObject> objects = getXObjects().get(classReference);
2516  107017 if (objects != null) {
2517  22176 for (BaseObject object : objects) {
2518  22178 if (object != null) {
2519  22172 result = object;
2520  22172 break;
2521    }
2522    }
2523    }
2524  107016 return result;
2525    }
2526   
2527    /**
2528    * Get an object of this document based on its reference.
2529    *
2530    * @param objectReference the reference of the object
2531    * @return the XWiki object
2532    * @since 3.2M1
2533    */
 
2534  9845 toggle public BaseObject getXObject(ObjectReference objectReference)
2535    {
2536  9844 BaseObjectReference baseObjectReference;
2537  9847 if (objectReference instanceof BaseObjectReference) {
2538  3130 baseObjectReference = (BaseObjectReference) objectReference;
2539    } else {
2540  6717 baseObjectReference = new BaseObjectReference(objectReference);
2541    }
2542   
2543    // If the baseObjectReference has an object number, we return the object with this number,
2544    // otherwise, we consider it should be the first object, as specified by BaseObjectReference#getObjectNumber
2545  9849 return baseObjectReference.getObjectNumber() == null ? this.getXObject(baseObjectReference.getXClassReference())
2546    : getXObject(baseObjectReference.getXClassReference(), baseObjectReference.getObjectNumber());
2547    }
2548   
2549    /**
2550    * Get an object property of this document based on its reference.
2551    *
2552    * @param objectPropertyReference the reference of the object property
2553    * @return the object property
2554    * @since 3.2M3
2555    */
 
2556  2401 toggle public BaseProperty<ObjectPropertyReference> getXObjectProperty(ObjectPropertyReference objectPropertyReference)
2557    {
2558  2401 BaseObject object = getXObject((ObjectReference) objectPropertyReference.getParent());
2559   
2560  2401 if (object != null) {
2561  2400 return (BaseProperty<ObjectPropertyReference>) object.getField(objectPropertyReference.getName());
2562    }
2563   
2564  1 return null;
2565    }
2566   
2567    /**
2568    * @deprecated since 2.2M1 use {@link #getXObject(DocumentReference)} instead
2569    */
 
2570  13853 toggle @Deprecated
2571    public BaseObject getObject(String className)
2572    {
2573  13854 return getXObject(resolveClassReference(className));
2574    }
2575   
2576    /**
2577    * @since 2.2M1
2578    */
 
2579  22352 toggle public BaseObject getXObject(DocumentReference classReference, int nb)
2580    {
2581  22353 List<BaseObject> objects = getXObjects().get(classReference);
2582   
2583  22353 if (objects != null && objects.size() > nb) {
2584  17487 return objects.get(nb);
2585    }
2586   
2587  4867 return null;
2588    }
2589   
2590    /**
2591    * Get an xobject with the passed xclass at the passed location.
2592    * <p>
2593    * If <code>create</code> is true and the is no xobject at the passed located, it's created.
2594    *
2595    * @param classReference the xlcass of the object to retrieve
2596    * @param number the location of the xobject
2597    * @param create if true the xobject is created when it does not exist
2598    * @param xcontext the XWiki context
2599    * @return a {@link BaseObject} stored at passed location
2600    * @throws XWikiException when failing to create new xobject instance
2601    * @since 7.3M1
2602    * @since 7.2.1
2603    * @since 7.1.3
2604    * @since 6.4.6
2605    */
 
2606  0 toggle public BaseObject getXObject(EntityReference classReference, int number, boolean create, XWikiContext xcontext)
2607    throws XWikiException
2608    {
2609  0 DocumentReference absoluteClassReference = resolveClassReference(classReference);
2610   
2611  0 BaseObject xobject = getXObject(absoluteClassReference);
2612   
2613  0 if (xobject == null && create) {
2614  0 xobject = BaseClass.newCustomClassInstance(absoluteClassReference, xcontext);
2615   
2616  0 setXObject(number, xobject);
2617    }
2618   
2619  0 return xobject;
2620    }
2621   
2622    /**
2623    * @since 4.1M1
2624    */
 
2625  54 toggle public BaseObject getXObject(EntityReference classReference, int nb)
2626    {
2627  54 return getXObject(
2628    getCurrentReferenceDocumentReferenceResolver().resolve(classReference, getDocumentReference()), nb);
2629    }
2630   
2631    /**
2632    * @deprecated since 2.2M1 use {@link #getXObject(DocumentReference, int)} instead
2633    */
 
2634  8047 toggle @Deprecated
2635    public BaseObject getObject(String className, int nb)
2636    {
2637  8048 return getXObject(resolveClassReference(className), nb);
2638    }
2639   
2640    /**
2641    * @since 2.2M1
2642    */
 
2643  0 toggle public BaseObject getXObject(DocumentReference classReference, String key, String value)
2644    {
2645  0 return getXObject(classReference, key, value, false);
2646    }
2647   
2648    /**
2649    * @deprecated since 2.2M1 use {@link #getXObject(DocumentReference, String, String)} instead
2650    */
 
2651  241 toggle @Deprecated
2652    public BaseObject getObject(String className, String key, String value)
2653    {
2654  241 return getObject(className, key, value, false);
2655    }
2656   
2657    /**
2658    * @return 6.3M1
2659    */
 
2660  12747 toggle public BaseObject getXObject(EntityReference reference, String key, String value, boolean failover)
2661    {
2662  12745 if (reference instanceof DocumentReference) {
2663  0 return getXObject((DocumentReference) reference, key, value, failover);
2664  12744 } else if (reference.getType() == EntityType.DOCUMENT) {
2665    // class reference
2666  12744 return getXObject(getCurrentReferenceDocumentReferenceResolver().resolve(reference, getDocumentReference()),
2667    key, value, failover);
2668    }
2669   
2670  0 return null;
2671    }
2672   
2673    /**
2674    * @since 2.2M1
2675    */
 
2676  13164 toggle public BaseObject getXObject(DocumentReference classReference, String key, String value, boolean failover)
2677    {
2678  13165 try {
2679  13167 if (value == null) {
2680  0 if (failover) {
2681  0 return getXObject(classReference);
2682    } else {
2683  0 return null;
2684    }
2685    }
2686   
2687  13165 List<BaseObject> objects = getXObjects().get(classReference);
2688  13161 if ((objects == null) || (objects.size() == 0)) {
2689  11690 return null;
2690    }
2691  1476 for (BaseObject obj : objects) {
2692  1687 if (obj != null) {
2693  1687 if (value.equals(obj.getStringValue(key))) {
2694  277 return obj;
2695    }
2696    }
2697    }
2698   
2699  1199 if (failover) {
2700  1174 return getXObject(classReference);
2701    } else {
2702  25 return null;
2703    }
2704    } catch (Exception e) {
2705  0 if (failover) {
2706  0 return getXObject(classReference);
2707    }
2708   
2709  0 LOGGER.warn("Exception while accessing objects for document [{}]: {}", getDocumentReference(),
2710    e.getMessage(), e);
2711  0 return null;
2712    }
2713    }
2714   
2715    /**
2716    * @deprecated since 2.2M1 use {@link #getXObject(DocumentReference, String, String, boolean)} instead
2717    */
 
2718  404 toggle @Deprecated
2719    public BaseObject getObject(String className, String key, String value, boolean failover)
2720    {
2721  404 return getXObject(resolveClassReference(className), key, value, failover);
2722    }
2723   
2724    /**
2725    * @since 2.2M1
2726    * @deprecated use {@link #addXObject(BaseObject)} instead
2727    */
 
2728  615 toggle @Deprecated
2729    public void addXObject(DocumentReference classReference, BaseObject object)
2730    {
2731  615 List<BaseObject> vobj = this.xObjects.get(classReference);
2732  615 if (vobj == null) {
2733  231 setXObject(classReference, 0, object);
2734    } else {
2735  384 setXObject(classReference, vobj.size(), object);
2736    }
2737    }
2738   
2739    /**
2740    * Add the object to the document.
2741    *
2742    * @param object the xobject to add
2743    * @throws NullPointerException if the specified object is null because we need the get the class reference from the
2744    * object
2745    * @since 2.2.3
2746    */
 
2747  31339 toggle public void addXObject(BaseObject object)
2748    {
2749  31338 object.setOwnerDocument(this);
2750   
2751  31340 List<BaseObject> vobj = this.xObjects.get(object.getXClassReference());
2752  31339 if (vobj == null) {
2753  26829 setXObject(0, object);
2754    } else {
2755  4510 setXObject(vobj.size(), object);
2756    }
2757    }
2758   
2759    /**
2760    * @deprecated since 2.2M1 use {@link #addXObject(BaseObject)} instead
2761    */
 
2762  9 toggle @Deprecated
2763    public void addObject(String className, BaseObject object)
2764    {
2765  9 addXObject(resolveClassReference(className), object);
2766    }
2767   
2768    /**
2769    * @since 2.2M1
2770    * @deprecated use {@link #setXObject(int, BaseObject)} instead
2771    */
 
2772  616 toggle @Deprecated
2773    public void setXObject(DocumentReference classReference, int nb, BaseObject object)
2774    {
2775  616 if (object != null) {
2776  15 object.setOwnerDocument(this);
2777  15 object.setNumber(nb);
2778    }
2779   
2780  616 List<BaseObject> objects = this.xObjects.get(classReference);
2781  616 if (objects == null) {
2782  232 objects = new ArrayList<BaseObject>();
2783  232 this.xObjects.put(classReference, objects);
2784    }
2785  1232 while (nb >= objects.size()) {
2786  616 objects.add(null);
2787    }
2788  616 objects.set(nb, object);
2789  616 setMetaDataDirty(true);
2790    }
2791   
2792    /**
2793    * Replaces the object at the specified position and for the specified object's xclass.
2794    *
2795    * @param nb index of the element to replace
2796    * @param object the xobject to insert
2797    * @throws NullPointerException if the specified object is null because we need the get the class reference from the
2798    * object
2799    * @since 2.2.3
2800    */
 
2801  39462 toggle public void setXObject(int nb, BaseObject object)
2802    {
2803  39462 object.setOwnerDocument(this);
2804  39463 object.setNumber(nb);
2805   
2806  39462 List<BaseObject> objects = this.xObjects.get(object.getXClassReference());
2807  39462 if (objects == null) {
2808  32565 objects = new ArrayList<BaseObject>();
2809  32564 this.xObjects.put(object.getXClassReference(), objects);
2810    }
2811  79060 while (nb >= objects.size()) {
2812  39595 objects.add(null);
2813    }
2814  39462 objects.set(nb, object);
2815  39458 setMetaDataDirty(true);
2816    }
2817   
2818    /**
2819    * @deprecated since 2.2M1 use {@link #setXObject(DocumentReference, int, BaseObject)} instead
2820    */
 
2821  0 toggle @Deprecated
2822    public void setObject(String className, int nb, BaseObject object)
2823    {
2824  0 setXObject(resolveClassReference(className), nb, object);
2825    }
2826   
2827    /**
2828    * @return true if the document is a new one (i.e. it has never been saved) or false otherwise
2829    */
 
2830  595287 toggle public boolean isNew()
2831    {
2832  595288 return this.isNew;
2833    }
2834   
 
2835  473712 toggle public void setNew(boolean aNew)
2836    {
2837  473704 this.isNew = aNew;
2838    }
2839   
2840    /**
2841    * @since 2.2M1
2842    */
 
2843  0 toggle public void mergeXClass(XWikiDocument templatedoc)
2844    {
2845  0 BaseClass bclass = getXClass();
2846  0 BaseClass tbclass = templatedoc.getXClass();
2847  0 if (tbclass != null) {
2848  0 if (bclass == null) {
2849  0 setXClass(tbclass.clone());
2850    } else {
2851  0 getXClass().merge(tbclass.clone());
2852    }
2853    }
2854  0 setMetaDataDirty(true);
2855    }
2856   
2857    /**
2858    * @deprecated since 2.2M1 use {@link #mergeXClass(XWikiDocument)} instead
2859    */
 
2860  0 toggle @Deprecated
2861    public void mergexWikiClass(XWikiDocument templatedoc)
2862    {
2863  0 mergeXClass(templatedoc);
2864    }
2865   
2866    /**
2867    * @since 2.2M1
2868    */
 
2869  37 toggle public void mergeXObjects(XWikiDocument templateDoc)
2870    {
2871  37 for (Map.Entry<DocumentReference, List<BaseObject>> entry : templateDoc.getXObjects().entrySet()) {
2872    // Documents can't have objects of types defined in a different wiki so we make sure the class reference
2873    // matches this document's wiki.
2874  31 DocumentReference classReference = entry.getKey().replaceParent(entry.getKey().getWikiReference(),
2875    getDocumentReference().getWikiReference());
2876    // Copy the objects from the template document only if this document doesn't have them already.
2877    //
2878    // Note: this might be a bit misleading since it will not add objects from the template if some objects of
2879    // that class already exist in the current document.
2880  31 if (getXObjectSize(classReference) == 0) {
2881  30 for (BaseObject object : entry.getValue()) {
2882  32 if (object != null) {
2883  32 addXObject(object.duplicate());
2884    }
2885    }
2886    }
2887    }
2888    }
2889   
2890    /**
2891    * @deprecated since 2.2M1 use {@link #mergeXObjects(XWikiDocument)} instead
2892    */
 
2893  0 toggle @Deprecated
2894    public void mergexWikiObjects(XWikiDocument templatedoc)
2895    {
2896  0 mergeXObjects(templatedoc);
2897    }
2898   
2899    /**
2900    * @since 2.2M1
2901    */
 
2902  31201 toggle public void cloneXObjects(XWikiDocument templatedoc)
2903    {
2904  31195 cloneXObjects(templatedoc, true);
2905    }
2906   
2907    /**
2908    * @since 2.2.3
2909    */
 
2910  131 toggle public void duplicateXObjects(XWikiDocument templatedoc)
2911    {
2912  131 cloneXObjects(templatedoc, false);
2913    }
2914   
2915    /**
2916    * Copy specified document objects into current document.
2917    *
2918    * @param templatedoc the document to copy
2919    * @param keepsIdentity if true it does an exact java copy, otherwise it duplicate objects with the new document
2920    * name (and new class names)
2921    */
 
2922  31324 toggle private void cloneXObjects(XWikiDocument templatedoc, boolean keepsIdentity)
2923    {
2924    // clean map
2925  31321 this.xObjects.clear();
2926   
2927    // fill map
2928  31326 for (Map.Entry<DocumentReference, List<BaseObject>> entry : templatedoc.getXObjects().entrySet()) {
2929  26991 List<BaseObject> tobjects = entry.getValue();
2930   
2931    // clone and insert xobjects
2932  26990 for (BaseObject otherObject : tobjects) {
2933  31825 if (otherObject != null) {
2934  31220 if (keepsIdentity) {
2935  31118 addXObject(otherObject.clone());
2936    } else {
2937  102 BaseObject newObject = otherObject.duplicate(getDocumentReference());
2938  102 setXObject(newObject.getNumber(), newObject);
2939    }
2940  602 } else if (keepsIdentity) {
2941    // set null object to make sure to have exactly the same thing when cloning a document
2942  600 addXObject(entry.getKey(), null);
2943    }
2944    }
2945    }
2946    }
2947   
2948    /**
2949    * @since 2.2M1
2950    */
 
2951  62685 toggle public DocumentReference getTemplateDocumentReference()
2952    {
2953  62687 return this.templateDocumentReference;
2954    }
2955   
2956    /**
2957    * @deprecated since 2.2M1 use {@link #getTemplateDocumentReference()} instead
2958    */
 
2959  0 toggle @Deprecated
2960    public String getTemplate()
2961    {
2962  0 String templateReferenceAsString;
2963  0 DocumentReference templateDocumentReference = getTemplateDocumentReference();
2964  0 if (templateDocumentReference != null) {
2965  0 templateReferenceAsString = LOCAL_REFERENCE_SERIALIZER.serialize(templateDocumentReference);
2966    } else {
2967  0 templateReferenceAsString = "";
2968    }
2969  0 return templateReferenceAsString;
2970    }
2971   
2972    /**
2973    * @since 2.2M1
2974    */
 
2975  31359 toggle public void setTemplateDocumentReference(DocumentReference templateDocumentReference)
2976    {
2977  31361 if (!Objects.equals(getTemplateDocumentReference(), templateDocumentReference)) {
2978  85 this.templateDocumentReference = templateDocumentReference;
2979  85 setMetaDataDirty(true);
2980    }
2981    }
2982   
2983    /**
2984    * @deprecated since 2.2M1 use {@link #setTemplateDocumentReference(DocumentReference)} instead
2985    */
 
2986  0 toggle @Deprecated
2987    public void setTemplate(String template)
2988    {
2989  0 DocumentReference templateReference = null;
2990  0 if (!StringUtils.isEmpty(template)) {
2991  0 templateReference = getCurrentMixedDocumentReferenceResolver().resolve(template);
2992    }
2993  0 setTemplateDocumentReference(templateReference);
2994    }
2995   
 
2996  0 toggle public String displayPrettyName(String fieldname, XWikiContext context)
2997    {
2998  0 return displayPrettyName(fieldname, false, true, context);
2999    }
3000   
 
3001  0 toggle public String displayPrettyName(String fieldname, boolean showMandatory, XWikiContext context)
3002    {
3003  0 return displayPrettyName(fieldname, showMandatory, true, context);
3004    }
3005   
 
3006  0 toggle public String displayPrettyName(String fieldname, boolean showMandatory, boolean before, XWikiContext context)
3007    {
3008  0 try {
3009  0 BaseObject object = getXObject();
3010  0 if (object == null) {
3011  0 object = getFirstObject(fieldname, context);
3012    }
3013  0 return displayPrettyName(fieldname, showMandatory, before, object, context);
3014    } catch (Exception e) {
3015  0 return "";
3016    }
3017    }
3018   
 
3019  454 toggle public String displayPrettyName(String fieldname, BaseObject obj, XWikiContext context)
3020    {
3021  454 return displayPrettyName(fieldname, false, true, obj, context);
3022    }
3023   
 
3024  0 toggle public String displayPrettyName(String fieldname, boolean showMandatory, BaseObject obj, XWikiContext context)
3025    {
3026  0 return displayPrettyName(fieldname, showMandatory, true, obj, context);
3027    }
3028   
 
3029  454 toggle public String displayPrettyName(String fieldname, boolean showMandatory, boolean before, BaseObject obj,
3030    XWikiContext context)
3031    {
3032  454 try {
3033  454 PropertyClass pclass = (PropertyClass) obj.getXClass(context).get(fieldname);
3034  454 String dprettyName = "";
3035  454 if (showMandatory) {
3036  0 dprettyName = context.getWiki().addMandatory(context);
3037    }
3038  454 if (before) {
3039  454 return dprettyName + pclass.getPrettyName(context);
3040    } else {
3041  0 return pclass.getPrettyName(context) + dprettyName;
3042    }
3043    } catch (Exception e) {
3044  0 return "";
3045    }
3046    }
3047   
 
3048  0 toggle public String displayTooltip(String fieldname, XWikiContext context)
3049    {
3050  0 try {
3051  0 BaseObject object = getXObject();
3052  0 if (object == null) {
3053  0 object = getFirstObject(fieldname, context);
3054    }
3055  0 return displayTooltip(fieldname, object, context);
3056    } catch (Exception e) {
3057  0 return "";
3058    }
3059    }
3060   
 
3061  0 toggle public String displayTooltip(String fieldname, BaseObject obj, XWikiContext context)
3062    {
3063  0 String result = "";
3064   
3065  0 try {
3066  0 PropertyClass pclass = (PropertyClass) obj.getXClass(context).get(fieldname);
3067  0 String tooltip = pclass.getTooltip(context);
3068  0 if ((tooltip != null) && (!tooltip.trim().equals(""))) {
3069  0 String img = "<img src=\"" + context.getWiki().getSkinFile("info.gif", context)
3070    + "\" class=\"tooltip_image\" align=\"middle\" />";
3071  0 result = context.getWiki().addTooltip(img, tooltip, context);
3072    }
3073    } catch (Exception e) {
3074   
3075    }
3076   
3077  0 return result;
3078    }
3079   
3080    /**
3081    * @param fieldname the name of the field to display
3082    * @param context the XWiki context
3083    * @return the rendered field
3084    */
 
3085  43 toggle public String display(String fieldname, XWikiContext context)
3086    {
3087  43 String result = "";
3088   
3089  43 try {
3090  43 BaseObject object = getXObject();
3091  43 if (object == null) {
3092  41 object = getFirstObject(fieldname, context);
3093    }
3094   
3095  43 result = display(fieldname, object, context);
3096    } catch (Exception e) {
3097  0 LOGGER.error("Failed to display field [" + fieldname + "] of document ["
3098    + getDefaultEntityReferenceSerializer().serialize(getDocumentReference()) + "]", e);
3099    }
3100   
3101  43 return result;
3102    }
3103   
3104    /**
3105    * @param fieldname the name of the field to display
3106    * @param obj the object containing the field to display
3107    * @param context the XWiki context
3108    * @return the rendered field
3109    */
 
3110  870 toggle public String display(String fieldname, BaseObject obj, XWikiContext context)
3111    {
3112  870 String type = null;
3113  870 try {
3114  870 type = (String) context.get("display");
3115    } catch (Exception e) {
3116    }
3117   
3118  870 if (type == null) {
3119  700 type = "view";
3120    }
3121   
3122  870 return display(fieldname, type, obj, context);
3123    }
3124   
3125    /**
3126    * @param fieldname the name of the field to display
3127    * @param mode the mode to use ("view", "edit", ...)
3128    * @param context the XWiki context
3129    * @return the rendered field
3130    */
 
3131  9 toggle public String display(String fieldname, String mode, XWikiContext context)
3132    {
3133  9 return display(fieldname, mode, "", context);
3134    }
3135   
3136    /**
3137    * @param fieldname the name of the field to display
3138    * @param type the type of the field to display
3139    * @param obj the object containing the field to display
3140    * @param context the XWiki context
3141    * @return the rendered field
3142    */
 
3143  1849 toggle public String display(String fieldname, String type, BaseObject obj, XWikiContext context)
3144    {
3145  1849 return display(fieldname, type, "", obj, context);
3146    }
3147   
3148    /**
3149    * @param fieldname the name of the field to display
3150    * @param mode the mode to use ("view", "edit", ...)
3151    * @param prefix the prefix to add in the field identifier in edit display for example
3152    * @param context the XWiki context
3153    * @return the rendered field
3154    */
 
3155  9 toggle public String display(String fieldname, String mode, String prefix, XWikiContext context)
3156    {
3157  9 try {
3158  9 BaseObject object = getXObject();
3159  9 if (object == null) {
3160  0 object = getFirstObject(fieldname, context);
3161    }
3162  9 if (object == null) {
3163  0 return "";
3164    } else {
3165  9 return display(fieldname, mode, prefix, object, context);
3166    }
3167    } catch (Exception e) {
3168  0 return "";
3169    }
3170    }
3171   
3172    /**
3173    * @param fieldname the name of the field to display
3174    * @param type the type of the field to display
3175    * @param obj the object containing the field to display
3176    * @param wrappingSyntaxId the syntax of the content in which the result will be included. This to take care of some
3177    * escaping depending of the syntax.
3178    * @param context the XWiki context
3179    * @return the rendered field
3180    */
 
3181  0 toggle public String display(String fieldname, String type, BaseObject obj, String wrappingSyntaxId, XWikiContext context)
3182    {
3183  0 return display(fieldname, type, "", obj, wrappingSyntaxId, context);
3184    }
3185   
3186    /**
3187    * @param fieldname the name of the field to display
3188    * @param type the type of the field to display
3189    * @param pref the prefix to add in the field identifier in edit display for example
3190    * @param obj the object containing the field to display
3191    * @param context the XWiki context
3192    * @return the rendered field
3193    */
 
3194  1858 toggle public String display(String fieldname, String type, String pref, BaseObject obj, XWikiContext context)
3195    {
3196  1858 return display(fieldname, type, pref, obj, context.getWiki().getCurrentContentSyntaxId(getSyntaxId(), context),
3197    context);
3198    }
3199   
3200    /**
3201    * @param fieldname the name of the field to display
3202    * @param type the type of the field to display
3203    * @param pref the prefix to add in the field identifier in edit display for example
3204    * @param obj the object containing the field to display
3205    * @param wrappingSyntaxId the syntax of the content in which the result will be included. This to take care of some
3206    * escaping depending of the syntax.
3207    * @param context the XWiki context
3208    * @return the rendered field
3209    */
 
3210  1858 toggle public String display(String fieldname, String type, String pref, BaseObject obj, String wrappingSyntaxId,
3211    XWikiContext context)
3212    {
3213  1858 if (obj == null) {
3214  0 return "";
3215    }
3216   
3217  1858 boolean isInRenderingEngine = BooleanUtils.toBoolean((Boolean) context.get("isInRenderingEngine"));
3218  1858 HashMap<String, Object> backup = new HashMap<String, Object>();
3219  1858 try {
3220  1858 backupContext(backup, context);
3221  1858 setAsContextDoc(context);
3222   
3223    // Make sure to execute with the right of the document author instead of the content author
3224    // (because modifying object property does not modify content author)
3225  1858 XWikiDocument sdoc = context.getDoc();
3226  1858 if (sdoc != null && !Objects.equals(sdoc.getContentAuthorReference(), sdoc.getAuthorReference())) {
3227    // Hack the sdoc to make test module believe the content author is the author
3228  24 sdoc = sdoc.clone();
3229  24 sdoc.setContentAuthorReference(sdoc.getAuthorReference());
3230  24 context.put(CKEY_SDOC, sdoc);
3231    }
3232   
3233  1858 type = type.toLowerCase();
3234  1858 StringBuffer result = new StringBuffer();
3235  1858 PropertyClass pclass = (PropertyClass) obj.getXClass(context).get(fieldname);
3236  1858 String prefix = pref + LOCAL_REFERENCE_SERIALIZER.serialize(obj.getXClass(context).getDocumentReference())
3237    + "_" + obj.getNumber() + "_";
3238   
3239  1858 if (pclass == null) {
3240  0 return "";
3241  1858 } else if (pclass.isCustomDisplayed(context)) {
3242  166 pclass.displayCustom(result, fieldname, prefix, type, obj, context);
3243  1692 } else if (type.equals("view")) {
3244  736 pclass.displayView(result, fieldname, prefix, obj, context);
3245  956 } else if (type.equals("rendered")) {
3246  0 String fcontent = pclass.displayView(fieldname, prefix, obj, context);
3247    // This mode is deprecated for the new rendering and should also be removed for the old rendering
3248    // since the way to implement this now is to choose the type of rendering to do in the class itself.
3249    // Thus for the new rendering we simply make this mode work like the "view" mode.
3250  0 if (is10Syntax(wrappingSyntaxId)) {
3251  0 result.append(getRenderedContent(fcontent, getSyntaxId(), context));
3252    } else {
3253  0 result.append(fcontent);
3254    }
3255  956 } else if (type.equals("edit")) {
3256  956 context.addDisplayedField(fieldname);
3257    // If the Syntax id is "xwiki/1.0" then use the old rendering subsystem and prevent wiki syntax
3258    // rendering using the pre macro. In the new rendering system it's the XWiki Class itself that does the
3259    // escaping. For example for a textarea check the TextAreaClass class.
3260  956 if (is10Syntax(wrappingSyntaxId)) {
3261    // Don't use pre when not in the rendernig engine since for template we don't evaluate wiki syntax.
3262  1 if (isInRenderingEngine) {
3263  1 result.append("{pre}");
3264    }
3265    }
3266  956 pclass.displayEdit(result, fieldname, prefix, obj, context);
3267  956 if (is10Syntax(wrappingSyntaxId)) {
3268  1 if (isInRenderingEngine) {
3269  1 result.append("{/pre}");
3270    }
3271    }
3272  0 } else if (type.equals("hidden")) {
3273    // If the Syntax id is "xwiki/1.0" then use the old rendering subsystem and prevent wiki syntax
3274    // rendering using the pre macro. In the new rendering system it's the XWiki Class itself that does the
3275    // escaping. For example for a textarea check the TextAreaClass class.
3276  0 if (is10Syntax(wrappingSyntaxId) && isInRenderingEngine) {
3277  0 result.append("{pre}");
3278    }
3279  0 pclass.displayHidden(result, fieldname, prefix, obj, context);
3280  0 if (is10Syntax(wrappingSyntaxId) && isInRenderingEngine) {
3281  0 result.append("{/pre}");
3282    }
3283  0 } else if (type.equals("search")) {
3284    // Backward compatibility
3285   
3286    // Check if the method has been injected using aspects
3287  0 Method searchMethod = null;
3288  0 for (Method method : pclass.getClass().getMethods()) {
3289  0 if (method.getName().equals("displaySearch") && method.getParameterTypes().length == 5) {
3290  0 searchMethod = method;
3291  0 break;
3292    }
3293    }
3294   
3295  0 if (searchMethod != null) {
3296    // If the Syntax id is "xwiki/1.0" then use the old rendering subsystem and prevent wiki syntax
3297    // rendering using the pre macro. In the new rendering system it's the XWiki Class itself that does
3298    // the
3299    // escaping. For example for a textarea check the TextAreaClass class.
3300  0 if (is10Syntax(wrappingSyntaxId) && isInRenderingEngine) {
3301  0 result.append("{pre}");
3302    }
3303  0 prefix = LOCAL_REFERENCE_SERIALIZER.serialize(obj.getXClass(context).getDocumentReference()) + "_";
3304  0 searchMethod.invoke(pclass, result, fieldname, prefix, context.get("query"), context);
3305  0 if (is10Syntax(wrappingSyntaxId) && isInRenderingEngine) {
3306  0 result.append("{/pre}");
3307    }
3308    } else {
3309  0 pclass.displayView(result, fieldname, prefix, obj, context);
3310    }
3311    } else {
3312  0 pclass.displayView(result, fieldname, prefix, obj, context);
3313    }
3314   
3315    // If we're in new rendering engine we want to wrap the HTML returned by displayView() in
3316    // a {{html/}} macro so that the user doesn't have to do it.
3317    // We test if we're inside the rendering engine since it's also possible that this display() method is
3318    // called
3319    // directly from a template and in this case we only want HTML as a result and not wiki syntax.
3320    // TODO: find a more generic way to handle html macro because this works only for XWiki 1.0 and XWiki 2.0
3321    // Add the {{html}}{{/html}} only when result really contains html since it's not needed for pure text
3322  1858 if (isInRenderingEngine && !is10Syntax(wrappingSyntaxId)
3323    && (result.indexOf("<") != -1 || result.indexOf(">") != -1)) {
3324  381 result.insert(0, "{{html clean=\"false\" wiki=\"false\"}}");
3325  381 result.append("{{/html}}");
3326    }
3327   
3328  1858 return result.toString();
3329    } catch (Exception ex) {
3330    // TODO: It would better to check if the field exists rather than catching an exception
3331    // raised by a NPE as this is currently the case here...
3332  0 LOGGER.warn("Failed to display field [" + fieldname + "] in [" + type + "] mode for Object of Class ["
3333    + getDefaultEntityReferenceSerializer().serialize(obj.getDocumentReference()) + "]", ex);
3334  0 return "";
3335    } finally {
3336  1858 restoreContext(backup, context);
3337    }
3338    }
3339   
3340    /**
3341    * @since 2.2M1
3342    */
 
3343  0 toggle public String displayForm(DocumentReference classReference, String header, String format, XWikiContext context)
3344    {
3345  0 return displayForm(classReference, header, format, true, context);
3346    }
3347   
3348    /**
3349    * @deprecated since 2.2M1, use {@link #displayForm(DocumentReference, String, String, XWikiContext)} instead
3350    */
 
3351  0 toggle @Deprecated
3352    public String displayForm(String className, String header, String format, XWikiContext context)
3353    {
3354  0 return displayForm(className, header, format, true, context);
3355    }
3356   
3357    /**
3358    * @since 2.2M1
3359    */
 
3360  0 toggle public String displayForm(DocumentReference classReference, String header, String format, boolean linebreak,
3361    XWikiContext context)
3362    {
3363  0 List<BaseObject> objects = getXObjects(classReference);
3364  0 if (format.endsWith("\\n")) {
3365  0 linebreak = true;
3366    }
3367   
3368  0 BaseObject firstobject = null;
3369  0 Iterator<BaseObject> foit = objects.iterator();
3370  0 while ((firstobject == null) && foit.hasNext()) {
3371  0 firstobject = foit.next();
3372    }
3373   
3374  0 if (firstobject == null) {
3375  0 return "";
3376    }
3377   
3378  0 BaseClass bclass = firstobject.getXClass(context);
3379  0 if (bclass.getPropertyList().size() == 0) {
3380  0 return "";
3381    }
3382   
3383  0 StringBuilder result = new StringBuilder();
3384  0 VelocityContext vcontext = new VelocityContext();
3385  0 for (String propertyName : bclass.getPropertyList()) {
3386  0 PropertyClass pclass = (PropertyClass) bclass.getField(propertyName);
3387  0 vcontext.put(pclass.getName(), pclass.getPrettyName());
3388    }
3389  0 result.append(evaluate(header, context.getDoc().getPrefixedFullName(), vcontext, context));
3390  0 if (linebreak) {
3391  0 result.append("\n");
3392    }
3393   
3394    // display each line
3395  0 for (int i = 0; i < objects.size(); i++) {
3396  0 vcontext.put("id", Integer.valueOf(i + 1));
3397  0 BaseObject object = objects.get(i);
3398  0 if (object != null) {
3399  0 for (String name : bclass.getPropertyList()) {
3400  0 vcontext.put(name, display(name, object, context));
3401    }
3402  0 result.append(evaluate(format, context.getDoc().getPrefixedFullName(), vcontext, context));
3403  0 if (linebreak) {
3404  0 result.append("\n");
3405    }
3406    }
3407    }
3408  0 return result.toString();
3409    }
3410   
 
3411  0 toggle private String evaluate(String content, String name, VelocityContext vcontext, XWikiContext context)
3412    {
3413  0 StringWriter writer = new StringWriter();
3414  0 try {
3415  0 VelocityManager velocityManager = Utils.getComponent(VelocityManager.class);
3416  0 velocityManager.getVelocityEngine().evaluate(vcontext, writer, name, content);
3417  0 return writer.toString();
3418    } catch (Exception e) {
3419  0 LOGGER.error("Error while parsing velocity template namespace [{}]", name, e);
3420  0 Object[] args = { name };
3421  0 XWikiException xe = new XWikiException(XWikiException.MODULE_XWIKI_RENDERING,
3422    XWikiException.ERROR_XWIKI_RENDERING_VELOCITY_EXCEPTION, "Error while parsing velocity page {0}", e,
3423    args);
3424  0 return Util.getHTMLExceptionMessage(xe, context);
3425    }
3426    }
3427   
3428    /**
3429    * @deprecated since 2.2M1, use {@link #displayForm(DocumentReference, String, String, boolean, XWikiContext)}
3430    * instead
3431    */
 
3432  0 toggle @Deprecated
3433    public String displayForm(String className, String header, String format, boolean linebreak, XWikiContext context)
3434    {
3435  0 return displayForm(resolveClassReference(className), header, format, linebreak, context);
3436    }
3437   
3438    /**
3439    * @since 2.2M1
3440    */
 
3441  0 toggle public String displayForm(DocumentReference classReference, XWikiContext context)
3442    {
3443  0 List<BaseObject> objects = getXObjects(classReference);
3444  0 if (objects == null) {
3445  0 return "";
3446    }
3447   
3448  0 BaseObject firstobject = null;
3449  0 Iterator<BaseObject> foit = objects.iterator();
3450  0 while ((firstobject == null) && foit.hasNext()) {
3451  0 firstobject = foit.next();
3452    }
3453   
3454  0 if (firstobject == null) {
3455  0 return "";
3456    }
3457   
3458  0 BaseClass bclass = firstobject.getXClass(context);
3459  0 if (bclass.getPropertyList().size() == 0) {
3460  0 return "";
3461    }
3462   
3463  0 StringBuilder result = new StringBuilder();
3464  0 result.append("{table}\n");
3465  0 boolean first = true;
3466  0 for (String propertyName : bclass.getPropertyList()) {
3467  0 if (first == true) {
3468  0 first = false;
3469    } else {
3470  0 result.append("|");
3471    }
3472  0 PropertyClass pclass = (PropertyClass) bclass.getField(propertyName);
3473  0 result.append(pclass.getPrettyName());
3474    }
3475  0 result.append("\n");
3476  0 for (int i = 0; i < objects.size(); i++) {
3477  0 BaseObject object = objects.get(i);
3478  0 if (object != null) {
3479  0 first = true;
3480  0 for (String propertyName : bclass.getPropertyList()) {
3481  0 if (first == true) {
3482  0 first = false;
3483    } else {
3484  0 result.append("|");
3485    }
3486  0 String data = display(propertyName, object, context);
3487  0 data = data.trim();
3488  0 data = data.replaceAll("\n", " ");
3489  0 if (data.length() == 0) {
3490  0 result.append("&nbsp;");
3491    } else {
3492  0 result.append(data);
3493    }
3494    }
3495  0 result.append("\n");
3496    }
3497    }
3498  0 result.append("{table}\n");
3499  0 return result.toString();
3500    }
3501   
3502    /**
3503    * @deprecated since 2.2M1, use {@link #displayForm(DocumentReference, XWikiContext)} instead
3504    */
 
3505  0 toggle @Deprecated
3506    public String displayForm(String className, XWikiContext context)
3507    {
3508  0 return displayForm(resolveClassReference(className), context);
3509    }
3510   
 
3511  31332 toggle public boolean isFromCache()
3512    {
3513  31330 return this.fromCache;
3514    }
3515   
 
3516  179320 toggle public void setFromCache(boolean fromCache)
3517    {
3518  179321 this.fromCache = fromCache;
3519    }
3520   
 
3521  271 toggle public void readDocMetaFromForm(EditForm eform, XWikiContext context) throws XWikiException
3522    {
3523  271 String defaultLanguage = eform.getDefaultLanguage();
3524  271 if (defaultLanguage != null) {
3525  0 setDefaultLanguage(defaultLanguage);
3526    }
3527   
3528  271 String defaultTemplate = eform.getDefaultTemplate();
3529  271 if (defaultTemplate != null) {
3530  0 setDefaultTemplate(defaultTemplate);
3531    }
3532   
3533  271 String creator = eform.getCreator();
3534  271 if ((creator != null) && (!creator.equals(getCreator()))) {
3535  0 if ((getCreatorReference().equals(context.getUserReference()))
3536    || (context.getWiki().getRightService().hasAdminRights(context))) {
3537  0 setCreator(creator);
3538    }
3539    }
3540   
3541  271 String parent = eform.getParent();
3542  271 if (parent != null) {
3543  90 setParent(parent);
3544    }
3545   
3546    // Read the comment from the form
3547  271 String comment = eform.getComment();
3548  271 if (comment != null) {
3549  55 setComment(comment);
3550    }
3551   
3552    // Read the minor edit checkbox from the form
3553  271 setMinorEdit(eform.isMinorEdit());
3554   
3555  271 String tags = eform.getTags();
3556  271 if (!StringUtils.isEmpty(tags)) {
3557  0 setTags(tags, context);
3558    }
3559   
3560    // Set the Syntax id if defined
3561  271 String syntaxId = eform.getSyntaxId();
3562  271 if (syntaxId != null) {
3563  2 setSyntaxId(syntaxId);
3564    }
3565   
3566    // Read the hidden checkbox from the form
3567  271 if (eform.getHidden() != null) {
3568  3 setHidden("1".equals(eform.getHidden()));
3569    }
3570    }
3571   
3572    /**
3573    * add tags to the document.
3574    */
 
3575  0 toggle public void setTags(String tagsStr, XWikiContext context) throws XWikiException
3576    {
3577  0 BaseClass tagsClass = context.getWiki().getTagClass(context);
3578   
3579  0 StaticListClass tagProp = (StaticListClass) tagsClass.getField(XWikiConstant.TAG_CLASS_PROP_TAGS);
3580   
3581  0 BaseObject tags = getObject(XWikiConstant.TAG_CLASS, true, context);
3582   
3583  0 tags.safeput(XWikiConstant.TAG_CLASS_PROP_TAGS, tagProp.fromString(tagsStr));
3584   
3585  0 setMetaDataDirty(true);
3586    }
3587   
 
3588  0 toggle public String getTags(XWikiContext context)
3589    {
3590  0 ListProperty prop = (ListProperty) getTagProperty(context);
3591   
3592    // I don't know why we need to XML-escape the list of tags but for backwards compatibility we need to keep doing
3593    // this. When this method was added it was using ListProperty#getTextValue() which used to return
3594    // ListProperty#toFormString() before we fixed it to return the unescaped value because we need to save the raw
3595    // value in the database and ListProperty#getTextValue() is called when the list property is saved.
3596  0 return prop != null ? prop.toFormString() : "";
3597    }
3598   
 
3599  343 toggle public List<String> getTagsList(XWikiContext context)
3600    {
3601  343 List<String> tagList = null;
3602   
3603  343 BaseProperty prop = getTagProperty(context);
3604  343 if (prop != null) {
3605  4 tagList = (List<String>) prop.getValue();
3606    }
3607   
3608  343 return tagList;
3609    }
3610   
 
3611  343 toggle private BaseProperty getTagProperty(XWikiContext context)
3612    {
3613  343 BaseObject tags = getObject(XWikiConstant.TAG_CLASS);
3614   
3615  343 return tags != null ? ((BaseProperty) tags.safeget(XWikiConstant.TAG_CLASS_PROP_TAGS)) : null;
3616    }
3617   
 
3618  0 toggle public List<String> getTagsPossibleValues(XWikiContext context)
3619    {
3620  0 List<String> list;
3621   
3622  0 try {
3623  0 BaseClass tagsClass = context.getWiki().getTagClass(context);
3624   
3625  0 String possibleValues =
3626    ((StaticListClass) tagsClass.getField(XWikiConstant.TAG_CLASS_PROP_TAGS)).getValues();
3627   
3628  0 return ListClass.getListFromString(possibleValues);
3629    } catch (XWikiException e) {
3630  0 LOGGER.error("Failed to get tag class", e);
3631   
3632  0 list = Collections.emptyList();
3633    }
3634   
3635  0 return list;
3636    }
3637   
 
3638  142 toggle public void readTranslationMetaFromForm(EditForm eform, XWikiContext context) throws XWikiException
3639    {
3640  142 String content = eform.getContent();
3641  142 if (content != null) {
3642    // Cleanup in case we use HTMLAREA
3643    // content = context.getUtil().substitute("s/<br class=\\\"htmlarea\\\"\\/>/\\r\\n/g",
3644    // content);
3645  70 content = context.getUtil().substitute("s/<br class=\"htmlarea\" \\/>/\r\n/g", content);
3646  70 setContent(content);
3647    }
3648  142 String title = eform.getTitle();
3649  142 if (title != null) {
3650  76 setTitle(title);
3651    }
3652    }
3653   
 
3654  254 toggle public void readObjectsFromForm(EditForm eform, XWikiContext context) throws XWikiException
3655    {
3656  254 for (DocumentReference reference : getXObjects().keySet()) {
3657  158 List<BaseObject> oldObjects = getXObjects(reference);
3658  158 List<BaseObject> newObjects = new ArrayList<BaseObject>();
3659  335 while (newObjects.size() < oldObjects.size()) {
3660  177 newObjects.add(null);
3661    }
3662  335 for (int i = 0; i < oldObjects.size(); i++) {
3663  177 BaseObject oldobject = oldObjects.get(i);
3664  177 if (oldobject != null) {
3665  177 BaseClass baseclass = oldobject.getXClass(context);
3666  177 BaseObject newobject = (BaseObject) baseclass.fromMap(
3667    eform.getObject(
3668    LOCAL_REFERENCE_SERIALIZER.serialize(baseclass.getDocumentReference()) + "_" + i),
3669    oldobject);
3670  177 newobject.setNumber(oldobject.getNumber());
3671  177 newobject.setGuid(oldobject.getGuid());
3672  177 newobject.setOwnerDocument(this);
3673  177 newObjects.set(newobject.getNumber(), newobject);
3674    }
3675    }
3676  158 getXObjects().put(reference, newObjects);
3677    }
3678    }
3679   
3680    /**
3681    * Generate a map from the request parameters of the form '<spacename>.<classname>_<number>_<propertyname>' Keys of
3682    * this map will be the reference '<spacename>.<classname>' to the Class (for example, 'XWiki.XWikiRights'), the
3683    * content is a list where each element describe property for the object <number>. Element of the list is a map
3684    * where key is <propertyname> and content is the array of corresponding values. Example with a list of HTTP
3685    * parameters:
3686    * <ul>
3687    * <li>XWiki.XWikiRights_0_users=XWiki.Admin</li>
3688    * <li>XWiki.XWikiRights_0_users=XWiki.Me</li>
3689    * <li>XWiki.XWikiRights_0_groups=XWiki.XWikiAllGroup</li>
3690    * <li>XWiki.XWikiRights_1_user=XWiki.Admin</li>
3691    * <li>XWiki.XWikiUsers_1_name=Spirou</li>
3692    * </ul>
3693    * will result in the following map <code><pre>
3694    * {
3695    * "XWiki.XWikiRights": {
3696    * "0": {
3697    * "users": ["XWiki.Admin", "XWiki.Me"],
3698    * "groups": ["XWiki.XWikiAllGroup"]
3699    * },
3700    * "1": {
3701    * "users": ["XWiki.Admin"]
3702    * }
3703    * ],
3704    * "XWiki.XWikiUsers":
3705    * "1": {
3706    * "name": ["Spirou"]
3707    * }
3708    * ]
3709    * }
3710    * </pre></code>
3711    *
3712    * @param request is the input HTTP request that provides the parameters
3713    * @param context
3714    * @return a map containing ordered data
3715    */
 
3716  19 toggle private Map<DocumentReference, SortedMap<Integer, Map<String, String[]>>> parseRequestUpdateOrCreate(
3717    XWikiRequest request, XWikiContext context)
3718    {
3719  19 Map<DocumentReference, SortedMap<Integer, Map<String, String[]>>> result = new HashMap<>();
3720  19 @SuppressWarnings("unchecked")
3721    Map<String, String[]> allParameters = request.getParameterMap();
3722  19 for (Entry<String, String[]> parameter : allParameters.entrySet()) {
3723  93 Matcher matcher = XPROPERTY_REFERENCE_PATTERN.matcher(parameter.getKey());
3724  93 if (matcher.matches() == false) {
3725  56 continue;
3726    }
3727  37 Integer classNumber;
3728  37 String className = matcher.group(1);
3729  37 String classNumberAsString = matcher.group(2);
3730  37 String classPropertyName = matcher.group(3);
3731  37 DocumentReference classReference = getCurrentDocumentReferenceResolver().resolve(className);
3732  37 try {
3733  37 BaseClass xClass = context.getWiki().getDocument(classReference, context).getXClass();
3734  37 if (xClass.getPropertyList().contains(classPropertyName) == false) {
3735  2 continue;
3736    }
3737  35 classNumber = Integer.parseInt(classNumberAsString);
3738    } catch (XWikiException e) {
3739    // If the class page cannot be found, skip the property update
3740  0 LOGGER.warn("Failed to load document [{}], ignoring property update [{}]. Reason: [{}]", classReference,
3741    parameter.getKey(), ExceptionUtils.getRootCauseMessage(e));
3742  0 continue;
3743    } catch (NumberFormatException e) {
3744    // If the numner isn't valid, skip the property update
3745  1 LOGGER.warn("Invalid xobject number [{}], ignoring property update [{}].", classNumberAsString,
3746    parameter.getKey());
3747  1 continue;
3748    }
3749  34 SortedMap<Integer, Map<String, String[]>> objectMap = result.get(classReference);
3750  34 if (objectMap == null) {
3751  19 objectMap = new TreeMap<>();
3752  19 result.put(classReference, objectMap);
3753    }
3754    // Get the property from the right object #objectNumber of type 'objectName'; create it if they don't exist
3755  34 Map<String, String[]> object = objectMap.get(classNumber);
3756  34 if (object == null) {
3757  22 object = new HashMap<>();
3758  22 objectMap.put(classNumber, object);
3759    }
3760  34 object.put(classPropertyName, parameter.getValue());
3761    }
3762  19 return result;
3763    }
3764   
3765    /**
3766    * Create and/or update objects in a document given a list of HTTP parameters of the form {@code
3767    * <spacename>.<classname>_<number>_<propertyname>}. If the object already exists, the field is replace by the given
3768    * value. If the object doesn't exist in the document, it is created then the property {@code <propertyname>} is
3769    * initialized with the given value. An object is only created if the given {@code <number>} is 'one-more' than the
3770    * existing number of objects. For example, if the document already has 2 objects of type {@code Space.Class}, then
3771    * it will create a new object only with {@code Space.Class_2_prop=something}. Every other parameter like {@code
3772    * Space.Class_42_prop=foobar} for example, will be ignore.
3773    *
3774    * @param eform is form information that contains all the query parameters
3775    * @param context
3776    * @throws XWikiException
3777    * @since 7.1M1
3778    */
 
3779  19 toggle public void readObjectsFromFormUpdateOrCreate(EditForm eform, XWikiContext context) throws XWikiException
3780    {
3781  19 Map<DocumentReference, SortedMap<Integer, Map<String, String[]>>> fromRequest =
3782    parseRequestUpdateOrCreate(eform.getRequest(), context);
3783  19 for (Entry<DocumentReference, SortedMap<Integer, Map<String, String[]>>> requestClassEntries : fromRequest
3784    .entrySet()) {
3785  19 DocumentReference requestClassReference = requestClassEntries.getKey();
3786  19 SortedMap<Integer, Map<String, String[]>> requestObjectMap = requestClassEntries.getValue();
3787  19 for (Entry<Integer, Map<String, String[]>> requestObjectEntry : requestObjectMap.entrySet()) {
3788  22 Integer requestObjectNumber = requestObjectEntry.getKey();
3789  22 Map<String, String[]> requestObjectPropertyMap = requestObjectEntry.getValue();
3790  22 BaseObject oldObject = getXObject(requestClassReference, requestObjectNumber);
3791  22 if (oldObject == null) {
3792    // Create the object only if it has been numbered one more than the number of existing objects
3793  5 if (requestObjectPropertyMap != null) {
3794  5 oldObject = newXObject(requestClassReference, context);
3795    } else {
3796  0 break;
3797    }
3798    }
3799  22 BaseClass baseClass = oldObject.getXClass(context);
3800  22 BaseObject newObject = (BaseObject) baseClass.fromMap(requestObjectPropertyMap, oldObject);
3801  22 newObject.setNumber(oldObject.getNumber());
3802  22 newObject.setGuid(oldObject.getGuid());
3803  22 newObject.setOwnerDocument(this);
3804  22 setXObject(requestObjectNumber, newObject);
3805    }
3806    }
3807    }
3808   
 
3809  142 toggle public void readFromForm(EditForm eform, XWikiContext context) throws XWikiException
3810    {
3811  142 readDocMetaFromForm(eform, context);
3812  142 readTranslationMetaFromForm(eform, context);
3813  142 ObjectPolicyType objectPolicy = eform.getObjectPolicy();
3814  142 if (objectPolicy == null || objectPolicy.equals(ObjectPolicyType.UPDATE)) {
3815  124 readObjectsFromForm(eform, context);
3816  18 } else if (objectPolicy.equals(ObjectPolicyType.UPDATE_OR_CREATE)) {
3817  18 readObjectsFromFormUpdateOrCreate(eform, context);
3818    }
3819    }
3820   
 
3821  279 toggle public void readFromTemplate(EditForm eform, XWikiContext context) throws XWikiException
3822    {
3823  279 String template = eform.getTemplate();
3824  279 readFromTemplate(template, context);
3825    }
3826   
3827    /**
3828    * @since 2.2M1
3829    */
 
3830  420 toggle public void readFromTemplate(DocumentReference templateDocumentReference, XWikiContext context)
3831    throws XWikiException
3832    {
3833  420 if (templateDocumentReference != null) {
3834  34 String content = getContent();
3835  34 if (!content.equals("\n") && !content.equals("") && !isNew()) {
3836  0 Object[] args = { getDefaultEntityReferenceSerializer().serialize(getDocumentReference()) };
3837  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
3838    XWikiException.ERROR_XWIKI_APP_DOCUMENT_NOT_EMPTY,
3839    "Cannot add a template to document {0} because it already has content", null, args);
3840    } else {
3841  34 XWiki xwiki = context.getWiki();
3842  34 XWikiDocument templatedoc = xwiki.getDocument(templateDocumentReference, context);
3843  34 if (templatedoc.isNew()) {
3844  0 Object[] args = { getDefaultEntityReferenceSerializer().serialize(templateDocumentReference),
3845    getCompactEntityReferenceSerializer().serialize(getDocumentReference()) };
3846  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
3847    XWikiException.ERROR_XWIKI_APP_TEMPLATE_DOES_NOT_EXIST,
3848    "Template document {0} does not exist when adding to document {1}", null, args);
3849    } else {
3850  34 setTemplateDocumentReference(templateDocumentReference);
3851  34 setTitle(templatedoc.getTitle());
3852  34 setContent(templatedoc.getContent());
3853   
3854    // Set the new document syntax as the syntax of the template since the template content
3855    // is copied into the new document
3856  34 setSyntax(templatedoc.getSyntax());
3857   
3858    // If the parent is not set in the current document set the template parent as the parent.
3859  34 if (getParentReference() == null) {
3860  20 setParentReference(templatedoc.getRelativeParentReference());
3861    }
3862   
3863  34 if (isNew()) {
3864    // We might have received the objects from the cache and the template objects might have been
3865    // copied already we need to remove them
3866  34 setXObjects(new TreeMap<DocumentReference, List<BaseObject>>());
3867    }
3868    // Merge the external objects.
3869    // Currently the choice is not to merge the base class and object because it is not the prefered way
3870    // of using external classes and objects.
3871  34 mergeXObjects(templatedoc);
3872   
3873    // Copy the attachments from the template document.
3874  34 copyAttachments(templatedoc);
3875    }
3876    }
3877    }
3878    }
3879   
3880    /**
3881    * @deprecated since 2.2M1 use {@link #readFromTemplate(DocumentReference, XWikiContext)} instead
3882    */
 
3883  417 toggle @Deprecated
3884    public void readFromTemplate(String template, XWikiContext context) throws XWikiException
3885    {
3886    // Keep the same behavior for backward compatibility
3887  417 DocumentReference templateDocumentReference = null;
3888  417 if (StringUtils.isNotEmpty(template)) {
3889  31 templateDocumentReference = getCurrentMixedDocumentReferenceResolver().resolve(template);
3890    }
3891  417 readFromTemplate(templateDocumentReference, context);
3892    }
3893   
3894    /**
3895    * Use the document passed as parameter as the new identity for the current document.
3896    *
3897    * @param document the document containing the new identity
3898    * @throws XWikiException in case of error
3899    */
 
3900  5 toggle private void clone(XWikiDocument document)
3901    {
3902  5 this.id = document.id;
3903   
3904  5 setDocumentReference(document.getDocumentReference());
3905  5 setRCSVersion(document.getRCSVersion());
3906  5 setDocumentArchive(document.getDocumentArchive());
3907  5 setAuthorReference(document.getAuthorReference());
3908  5 setContentAuthorReference(document.getContentAuthorReference());
3909  5 setContent(document.getContent());
3910  5 setCreationDate(document.getCreationDate());
3911  5 setDate(document.getDate());
3912  5 setCustomClass(document.getCustomClass());
3913  5 setContentUpdateDate(document.getContentUpdateDate());
3914  5 setTitle(document.getTitle());
3915  5 setFormat(document.getFormat());
3916  5 setFromCache(document.isFromCache());
3917  5 setElements(document.getElements());
3918  5 setMeta(document.getMeta());
3919  5 setMostRecent(document.isMostRecent());
3920  5 setNew(document.isNew());
3921  5 setStore(document.getStore());
3922  5 setTemplateDocumentReference(document.getTemplateDocumentReference());
3923  5 setParentReference(document.getRelativeParentReference());
3924  5 setCreatorReference(document.getCreatorReference());
3925  5 setDefaultLocale(document.getDefaultLocale());
3926  5 setDefaultTemplate(document.getDefaultTemplate());
3927  5 setValidationScript(document.getValidationScript());
3928  5 setLocale(document.getLocale());
3929  5 setXClass(document.getXClass().clone());
3930  5 setXClassXML(document.getXClassXML());
3931  5 setComment(document.getComment());
3932  5 setMinorEdit(document.isMinorEdit());
3933  5 setSyntax(document.getSyntax());
3934  5 setHidden(document.isHidden());
3935   
3936  5 cloneXObjects(document);
3937  5 cloneAttachments(document);
3938   
3939  5 setContentDirty(document.isContentDirty());
3940  5 setMetaDataDirty(document.isMetaDataDirty());
3941   
3942  5 this.elements = document.elements;
3943   
3944  5 this.originalDocument = document.originalDocument;
3945    }
3946   
 
3947  31197 toggle @Override
3948    public XWikiDocument clone()
3949    {
3950  31196 return cloneInternal(getDocumentReference(), true);
3951    }
3952   
3953    /**
3954    * Duplicate this document and give it a new name.
3955    *
3956    * @since 2.2.3
3957    */
 
3958  131 toggle public XWikiDocument duplicate(DocumentReference newDocumentReference)
3959    {
3960  131 return cloneInternal(newDocumentReference, false);
3961    }
3962   
 
3963  31321 toggle private XWikiDocument cloneInternal(DocumentReference newDocumentReference, boolean keepsIdentity)
3964    {
3965  31322 XWikiDocument doc = null;
3966  31324 try {
3967  31321 Constructor<? extends XWikiDocument> constructor = getClass().getConstructor(DocumentReference.class);
3968  31326 doc = constructor.newInstance(newDocumentReference);
3969   
3970    // use version field instead of getRCSVersion because it returns "1.1" if version==null.
3971  31325 doc.version = this.version;
3972  31326 doc.id = this.id;
3973  31324 doc.setDocumentArchive(getDocumentArchive());
3974  31326 doc.setAuthorReference(getAuthorReference());
3975  31326 doc.setContentAuthorReference(getContentAuthorReference());
3976  31326 doc.setContent(getContent());
3977  31327 doc.setCreationDate(getCreationDate());
3978  31324 doc.setDate(getDate());
3979  31325 doc.setCustomClass(getCustomClass());
3980  31327 doc.setContentUpdateDate(getContentUpdateDate());
3981  31324 doc.setTitle(getTitle());
3982  31327 doc.setFormat(getFormat());
3983  31327 doc.setFromCache(isFromCache());
3984  31323 doc.setElements(getElements());
3985  31327 doc.setMeta(getMeta());
3986  31319 doc.setMostRecent(isMostRecent());
3987  31328 doc.setNew(isNew());
3988  31323 doc.setStore(getStore());
3989  31323 doc.setTemplateDocumentReference(getTemplateDocumentReference());
3990  31323 doc.setParentReference(getRelativeParentReference());
3991  31319 doc.setCreatorReference(getCreatorReference());
3992  31327 doc.setDefaultLocale(getDefaultLocale());
3993  31325 doc.setDefaultTemplate(getDefaultTemplate());
3994  31320 doc.setValidationScript(getValidationScript());
3995  31321 doc.setLocale(getLocale());
3996  31325 doc.setComment(getComment());
3997  31326 doc.setMinorEdit(isMinorEdit());
3998  31327 doc.setSyntax(getSyntax());
3999  31324 doc.setHidden(isHidden());
4000   
4001  31322 if (this.xClass != null) {
4002  20722 doc.setXClass(this.xClass.clone());
4003    }
4004   
4005  31323 if (keepsIdentity) {
4006  31187 doc.setXClassXML(getXClassXML());
4007  31196 doc.cloneXObjects(this);
4008  31191 doc.cloneAttachments(this);
4009    } else {
4010  131 doc.getXClass().setCustomMapping(null);
4011  131 doc.duplicateXObjects(this);
4012  131 doc.copyAttachments(this);
4013    }
4014   
4015  31322 doc.setContentDirty(isContentDirty());
4016  31321 doc.setMetaDataDirty(isMetaDataDirty());
4017   
4018  31322 doc.elements = this.elements;
4019   
4020  31322 doc.originalDocument = this.originalDocument;
4021    } catch (Exception e) {
4022    // This should not happen
4023  0 LOGGER.error("Exception while cloning document", e);
4024    }
4025  31321 return doc;
4026    }
4027   
4028    /**
4029    * Clone attachments from another document. This implementation expects that this document is the same as the other
4030    * document and thus attachments will be saved in the database in the same place as the ones which they are cloning.
4031    *
4032    * @param sourceDocument an XWikiDocument to copy attachments from
4033    */
 
4034  31198 toggle private void cloneAttachments(final XWikiDocument sourceDocument)
4035    {
4036  31200 this.getAttachmentList().clear();
4037  31196 for (XWikiAttachment attach : sourceDocument.getAttachmentList()) {
4038  56419 XWikiAttachment newAttach = (XWikiAttachment) attach.clone();
4039   
4040    // Document is set to this because if this document is renamed then the attachment will have a new id
4041    // and be saved somewhere different.
4042  56419 newAttach.setDoc(this);
4043   
4044  56419 this.getAttachmentList().add(newAttach);
4045    }
4046    }
4047   
4048    /**
4049    * Copy attachments from one document to another. This implementation expects that you are copying the attachment
4050    * from one document to another and thus it should be saved seperately from the original in the database.
4051    *
4052    * @param sourceDocument an XWikiDocument to copy attachments from
4053    */
 
4054  165 toggle public void copyAttachments(XWikiDocument sourceDocument)
4055    {
4056    // Note: when clearing the attachment list, we automatically mark the document's metadata as dirty.
4057  165 getAttachmentList().clear();
4058   
4059  165 Iterator<XWikiAttachment> attit = sourceDocument.getAttachmentList().iterator();
4060  166 while (attit.hasNext()) {
4061  1 XWikiAttachment attachment = attit.next();
4062  1 XWikiAttachment newattachment = (XWikiAttachment) attachment.clone();
4063  1 newattachment.setDoc(this);
4064   
4065    // We need to set the content of the attachment to be dirty because the dirty bit
4066    // is used to signal that there is a reason to save the copied attachment, otherwise
4067    // the copied attachment will be empty since the original attachment content is not
4068    // modified in this operation.
4069  1 if (newattachment.getAttachment_content() != null) {
4070  1 newattachment.getAttachment_content().setContentDirty(true);
4071    }
4072   
4073  1 getAttachmentList().add(newattachment);
4074    }
4075    }
4076   
4077    /**
4078    * Load attachment content from database.
4079    *
4080    * @param context the XWiki context
4081    * @throws XWikiException when failing to load attachments
4082    * @since 5.3M2
4083    */
 
4084  458 toggle public void loadAttachmentsContent(XWikiContext context) throws XWikiException
4085    {
4086  458 for (XWikiAttachment attachment : getAttachmentList()) {
4087  21 attachment.loadContent(context);
4088    }
4089    }
4090   
 
4091  126 toggle public void loadAttachments(XWikiContext context) throws XWikiException
4092    {
4093  126 for (XWikiAttachment attachment : getAttachmentList()) {
4094  0 attachment.loadContent(context);
4095  0 attachment.loadArchive(context);
4096    }
4097    }
4098   
 
4099  12 toggle @Override
4100    public boolean equals(Object object)
4101    {
4102    // Same Java object, they sure are equal
4103  12 if (this == object) {
4104  6 return true;
4105    }
4106   
4107    // Reference/language (document identifier)
4108   
4109  6 XWikiDocument doc = (XWikiDocument) object;
4110  6 if (!getDocumentReference().equals(doc.getDocumentReference())) {
4111  0 return false;
4112    }
4113   
4114  6 if (!getDefaultLocale().equals(doc.getDefaultLocale())) {
4115  0 return false;
4116    }
4117   
4118  6 if (!getLocale().equals(doc.getLocale())) {
4119  0 return false;
4120    }
4121   
4122  6 if (getTranslation() != doc.getTranslation()) {
4123  0 return false;
4124    }
4125   
4126    // Authors
4127   
4128  6 if (ObjectUtils.notEqual(getAuthorReference(), doc.getAuthorReference())) {
4129  1 return false;
4130    }
4131   
4132  5 if (ObjectUtils.notEqual(getContentAuthorReference(), doc.getContentAuthorReference())) {
4133  0 return false;
4134    }
4135   
4136  5 if (ObjectUtils.notEqual(getCreatorReference(), doc.getCreatorReference())) {
4137  0 return false;
4138    }
4139   
4140    // Version
4141   
4142  5 if (!getVersion().equals(doc.getVersion())) {
4143  0 return false;
4144    }
4145   
4146  5 if (getDate().getTime() != doc.getDate().getTime()) {
4147  0 return false;
4148    }
4149   
4150  5 if (getContentUpdateDate().getTime() != doc.getContentUpdateDate().getTime()) {
4151  0 return false;
4152    }
4153   
4154  5 if (getCreationDate().getTime() != doc.getCreationDate().getTime()) {
4155  0 return false;
4156    }
4157   
4158  5 if (!getComment().equals(doc.getComment())) {
4159  0 return false;
4160    }
4161   
4162  5 if (isMinorEdit() != doc.isMinorEdit()) {
4163  0 return false;
4164    }
4165   
4166    // Datas
4167   
4168  5 if (!equalsData(doc)) {
4169  1 return false;
4170    }
4171   
4172    // We consider that 2 documents are still equal even when they have different original
4173    // documents (see getOriginalDocument() for more details as to what is an original
4174    // document).
4175   
4176  4 return true;
4177    }
4178   
4179    /**
4180    * Same as {@link #equals(Object)} but only for actual datas of the document.
4181    * <p>
4182    * The goal being to compare two versions of the same document this method skip every version/reference/author
4183    * related information. For example it allows to compare a document comming from a another wiki and easily check if
4184    * thoses actually are the same thing whatever the plumbing differences.
4185    *
4186    * @param otherDocument the document to compare
4187    * @return true if bith documents have the same datas
4188    * @since 4.1.1
4189    */
 
4190  106 toggle public boolean equalsData(XWikiDocument otherDocument)
4191    {
4192    // Same Java object, they sure are equal
4193  106 if (this == otherDocument) {
4194  0 return true;
4195    }
4196   
4197  106 if (ObjectUtils.notEqual(getParentReference(), otherDocument.getParentReference())) {
4198  0 return false;
4199    }
4200   
4201  106 if (!getFormat().equals(otherDocument.getFormat())) {
4202  0 return false;
4203    }
4204   
4205  106 if (!getTitle().equals(otherDocument.getTitle())) {
4206  0 return false;
4207    }
4208   
4209  106 if (!getContent().equals(otherDocument.getContent())) {
4210  5 return false;
4211    }
4212   
4213  101 if (!getDefaultTemplate().equals(otherDocument.getDefaultTemplate())) {
4214  0 return false;
4215    }
4216   
4217  101 if (!getValidationScript().equals(otherDocument.getValidationScript())) {
4218  0 return false;
4219    }
4220   
4221  101 if (ObjectUtils.notEqual(getSyntax(), otherDocument.getSyntax())) {
4222  0 return false;
4223    }
4224   
4225  101 if (isHidden() != otherDocument.isHidden()) {
4226  0 return false;
4227    }
4228   
4229    // XClass
4230   
4231  101 if (!getXClass().equals(otherDocument.getXClass())) {
4232  20 return false;
4233    }
4234   
4235    // XObjects
4236   
4237  81 Set<DocumentReference> myObjectClassReferences = getXObjects().keySet();
4238  81 Set<DocumentReference> otherObjectClassReferences = otherDocument.getXObjects().keySet();
4239  81 if (!myObjectClassReferences.equals(otherObjectClassReferences)) {
4240  0 return false;
4241    }
4242   
4243  81 for (DocumentReference reference : myObjectClassReferences) {
4244  20 List<BaseObject> myObjects = getXObjects(reference);
4245  20 List<BaseObject> otherObjects = otherDocument.getXObjects(reference);
4246  20 if (myObjects.size() != otherObjects.size()) {
4247  0 return false;
4248    }
4249  45 for (int i = 0; i < myObjects.size(); i++) {
4250  25 if ((myObjects.get(i) == null && otherObjects.get(i) != null)
4251    || (myObjects.get(i) != null && otherObjects.get(i) == null)) {
4252  0 return false;
4253    }
4254  25 if (myObjects.get(i) == null && otherObjects.get(i) == null) {
4255  4 continue;
4256    }
4257  21 if (!myObjects.get(i).equals(otherObjects.get(i))) {
4258  0 return false;
4259    }
4260    }
4261    }
4262   
4263    // Attachments
4264  81 List<XWikiAttachment> attachments = getAttachmentList();
4265  81 List<XWikiAttachment> otherAttachments = otherDocument.getAttachmentList();
4266  81 if (attachments.size() != otherAttachments.size()) {
4267  0 return false;
4268    }
4269  81 for (XWikiAttachment attachment : attachments) {
4270  5 XWikiAttachment otherAttachment = otherDocument.getAttachment(attachment.getFilename());
4271  5 try {
4272  5 if (otherAttachment == null || !attachment.equalsData(otherAttachment, null)) {
4273  2 return false;
4274    }
4275    } catch (XWikiException e) {
4276  0 throw new RuntimeException(
4277    String.format("Failed to compare attachments with reference [%s]", attachment.getReference()), e);
4278    }
4279    }
4280   
4281  79 return true;
4282    }
4283   
4284    /**
4285    * Retrieve the document in the current context language as an XML string. The rendrered document content and all
4286    * XObjects are included. Document attachments and archived versions are excluded. You should prefer
4287    * toXML(OutputStream, true, true, false, false, XWikiContext)} or toXML(com.xpn.xwiki.util.XMLWriter, true, true,
4288    * false, false, XWikiContext) on the translated document when possible to reduce memory load.
4289    *
4290    * @param context current XWikiContext
4291    * @return a string containing an XML representation of the document in the current context language
4292    * @throws XWikiException when an error occurs during wiki operation
4293    */
 
4294  0 toggle public String getXMLContent(XWikiContext context) throws XWikiException
4295    {
4296  0 XWikiDocument tdoc = getTranslatedDocument(context);
4297  0 return tdoc.toXML(true, true, false, false, context);
4298    }
4299   
4300    /**
4301    * Retrieve the document as an XML string. All XObject are included. Rendered content, attachments and archived
4302    * version are excluded. You should prefer toXML(OutputStream, true, false, false, false, XWikiContext)} or
4303    * toXML(com.xpn.xwiki.util.XMLWriter, true, false, false, false, XWikiContext) when possible to reduce memory load.
4304    *
4305    * @param context current XWikiContext
4306    * @return a string containing an XML representation of the document
4307    * @throws XWikiException when an error occurs during wiki operation
4308    */
 
4309  6138 toggle public String toXML(XWikiContext context) throws XWikiException
4310    {
4311  6138 return toXML(true, false, false, false, context);
4312    }
4313   
4314    /**
4315    * Retrieve the document as an XML string. All XObjects attachments and archived version are included. Rendered
4316    * content is excluded. You should prefer toXML(OutputStream, true, false, true, true, XWikiContext)} or
4317    * toXML(com.xpn.xwiki.util.XMLWriter, true, false, true, true, XWikiContext) when possible to reduce memory load.
4318    *
4319    * @param context current XWikiContext
4320    * @return a string containing an XML representation of the document
4321    * @throws XWikiException when an error occurs during wiki operation
4322    */
 
4323  153 toggle public String toFullXML(XWikiContext context) throws XWikiException
4324    {
4325  153 return toXML(true, false, true, true, context);
4326    }
4327   
4328    /**
4329    * Serialize the document into a new entry of an ZipOutputStream in XML format. All XObjects and attachments are
4330    * included. Rendered content is excluded.
4331    *
4332    * @param zos the ZipOutputStream to write to
4333    * @param zipname the name of the new entry to create
4334    * @param withVersions if true, also include archived version of the document
4335    * @param context current XWikiContext
4336    * @throws XWikiException when an error occurs during xwiki operations
4337    * @throws IOException when an error occurs during streaming operations
4338    * @since 2.3M2
4339    * @deprecated since 4.1M2
4340    */
 
4341  0 toggle @Deprecated
4342    public void addToZip(ZipOutputStream zos, String zipname, boolean withVersions, XWikiContext context)
4343    throws XWikiException, IOException
4344    {
4345  0 ZipEntry zipentry = new ZipEntry(zipname);
4346  0 zos.putNextEntry(zipentry);
4347  0 toXML(zos, true, false, true, withVersions, context);
4348  0 zos.closeEntry();
4349    }
4350   
4351    /**
4352    * Serialize the document into a new entry of an ZipOutputStream in XML format. The new entry is named
4353    * 'LastSpaceName/DocumentName'. All XObjects and attachments are included. Rendered content is excluded.
4354    *
4355    * @param zos the ZipOutputStream to write to
4356    * @param withVersions if true, also include archived version of the document
4357    * @param context current XWikiContext
4358    * @throws XWikiException when an error occurs during xwiki operations
4359    * @throws IOException when an error occurs during streaming operations
4360    * @since 2.3M2
4361    * @deprecated since 4.2M2
4362    */
 
4363  0 toggle @Deprecated
4364    public void addToZip(ZipOutputStream zos, boolean withVersions, XWikiContext context)
4365    throws XWikiException, IOException
4366    {
4367  0 String zipname =
4368    getDocumentReference().getLastSpaceReference().getName() + "/" + getDocumentReference().getName();
4369  0 String language = getLanguage();
4370  0 if (!StringUtils.isEmpty(language)) {
4371  0 zipname += "." + language;
4372    }
4373  0 addToZip(zos, zipname, withVersions, context);
4374    }
4375   
4376    /**
4377    * Serialize the document into a new entry of an ZipOutputStream in XML format. The new entry is named
4378    * 'LastSpaceName/DocumentName'. All XObjects, attachments and archived versions are included. Rendered content is
4379    * excluded.
4380    *
4381    * @param zos the ZipOutputStream to write to
4382    * @param context current XWikiContext
4383    * @throws XWikiException when an error occurs during xwiki operations
4384    * @throws IOException when an error occurs during streaming operations
4385    * @since 2.3M2
4386    * @deprecated since 4.1M2
4387    */
 
4388  0 toggle @Deprecated
4389    public void addToZip(ZipOutputStream zos, XWikiContext context) throws XWikiException, IOException
4390    {
4391  0 addToZip(zos, true, context);
4392    }
4393   
4394    /**
4395    * Serialize the document to an XML string. You should prefer
4396    * {@link #toXML(OutputStream, boolean, boolean, boolean, boolean, XWikiContext)} or
4397    * {@link #toXML(com.xpn.xwiki.internal.xml.XMLWriter, boolean, boolean, boolean, boolean, XWikiContext)} when
4398    * possible to reduce memory load.
4399    *
4400    * @param bWithObjects include XObjects
4401    * @param bWithRendering include the rendered content
4402    * @param bWithAttachmentContent include attachments content
4403    * @param bWithVersions include archived versions
4404    * @param context current XWikiContext
4405    * @return a string containing an XML representation of the document
4406    * @throws XWikiException when an errors occurs during wiki operations
4407    */
 
4408  6306 toggle public String toXML(boolean bWithObjects, boolean bWithRendering, boolean bWithAttachmentContent,
4409    boolean bWithVersions, XWikiContext context) throws XWikiException
4410    {
4411  6306 StringWriter writer = new StringWriter();
4412  6306 toXML(new DefaultWriterOutputTarget(writer), bWithObjects, bWithRendering, bWithAttachmentContent,
4413    bWithVersions, context);
4414  6306 return writer.toString();
4415    }
4416   
4417    /**
4418    * Serialize the document to an XML {@link DOMDocument}. All XObject are included. Rendered content, attachments and
4419    * archived version are excluded. You should prefer toXML(OutputStream, true, false, false, false, XWikiContext)} or
4420    * toXML(com.xpn.xwiki.util.XMLWriter, true, false, false, false, XWikiContext) when possible to reduce memory load.
4421    *
4422    * @param context current XWikiContext
4423    * @return a {@link DOMDocument} containing the serialized document.
4424    * @throws XWikiException when an errors occurs during wiki operations
4425    * @deprecated since 9.0RC1, use {@link #toXML(OutputTarget, boolean, boolean, boolean, boolean, XWikiContext)}
4426    * instead
4427    */
 
4428  1 toggle @Deprecated
4429    public Document toXMLDocument(XWikiContext context) throws XWikiException
4430    {
4431  1 return toXMLDocument(true, false, false, false, context);
4432    }
4433   
4434    /**
4435    * Serialize the document to an XML {@link DOMDocument}. You should prefer
4436    * {@link #toXML(OutputStream, boolean, boolean, boolean, boolean, XWikiContext)} or
4437    * {@link #toXML(com.xpn.xwiki.internal.xml.XMLWriter, boolean, boolean, boolean, boolean, XWikiContext)} when
4438    * possible to reduce memory load.
4439    *
4440    * @param bWithObjects include XObjects
4441    * @param bWithRendering include the rendered content
4442    * @param bWithAttachmentContent include attachments content
4443    * @param bWithVersions include archived versions
4444    * @param context current XWikiContext
4445    * @return a {@link DOMDocument} containing the serialized document.
4446    * @throws XWikiException when an errors occurs during wiki operations
4447    * @deprecated since 9.0RC1, use {@link #toXML(OutputTarget, boolean, boolean, boolean, boolean, XWikiContext)}
4448    * instead
4449    */
 
4450  1 toggle @Deprecated
4451    public Document toXMLDocument(boolean bWithObjects, boolean bWithRendering, boolean bWithAttachmentContent,
4452    boolean bWithVersions, XWikiContext context) throws XWikiException
4453    {
4454  1 Document doc = new DOMDocument();
4455  1 DOMXMLWriter wr = new DOMXMLWriter(doc, new OutputFormat("", true, context.getWiki().getEncoding()));
4456   
4457  1 try {
4458  1 toXML(wr, bWithObjects, bWithRendering, bWithAttachmentContent, bWithVersions, context);
4459  1 return doc;
4460    } catch (IOException e) {
4461  0 throw new RuntimeException(e);
4462    }
4463    }
4464   
4465    /**
4466    * Serialize the document to a {@link com.xpn.xwiki.internal.xml.XMLWriter}.
4467    *
4468    * @param bWithObjects include XObjects
4469    * @param bWithRendering include the rendered content
4470    * @param bWithAttachmentContent include attachments content
4471    * @param bWithVersions include archived versions
4472    * @param context current XWikiContext
4473    * @throws XWikiException when an errors occurs during wiki operations
4474    * @throws IOException when an errors occurs during streaming operations
4475    * @since 2.3M2
4476    * @deprecated since 9.0RC1, use {@link #toXML(OutputTarget, boolean, boolean, boolean, boolean, XWikiContext)}
4477    * instead
4478    */
 
4479  1 toggle @Deprecated
4480    public void toXML(XMLWriter wr, boolean bWithObjects, boolean bWithRendering, boolean bWithAttachmentContent,
4481    boolean bWithVersions, XWikiContext context) throws XWikiException, IOException
4482    {
4483    // IMPORTANT: we don't use directly XMLWriter's SAX apis here because it's not really working well
4484  1 DocumentResult domResult = new DocumentResult();
4485   
4486  1 toXML(new DefaultResultOutputTarget(domResult), bWithObjects, bWithRendering, bWithAttachmentContent,
4487    bWithVersions, context);
4488   
4489  1 wr.write(domResult.getDocument().getRootElement());
4490    }
4491   
4492    /**
4493    * Serialize the document to an OutputStream.
4494    *
4495    * @param bWithObjects include XObjects
4496    * @param bWithRendering include the rendered content
4497    * @param bWithAttachmentContent include attachments content
4498    * @param bWithVersions include archived versions
4499    * @param context current XWikiContext
4500    * @throws XWikiException when an errors occurs during wiki operations
4501    * @throws IOException when an errors occurs during streaming operations
4502    * @since 2.3M2
4503    */
 
4504  25 toggle public void toXML(OutputStream out, boolean bWithObjects, boolean bWithRendering, boolean bWithAttachmentContent,
4505    boolean bWithVersions, XWikiContext context) throws XWikiException, IOException
4506    {
4507  25 toXML(new DefaultOutputStreamOutputTarget(out), bWithObjects, bWithRendering, bWithAttachmentContent,
4508    bWithVersions, context);
4509    }
4510   
4511    /**
4512    * Serialize the document to an OutputStream.
4513    *
4514    * @param out the output where to write the XML
4515    * @param bWithObjects include XObjects
4516    * @param bWithRendering include the rendered content
4517    * @param bWithAttachmentContent include attachments content
4518    * @param bWithVersions include archived versions
4519    * @param context current XWikiContext
4520    * @throws XWikiException when an errors occurs during wiki operations
4521    * @since 9.0RC1
4522    */
 
4523  6332 toggle public void toXML(OutputTarget out, boolean bWithObjects, boolean bWithRendering, boolean bWithAttachmentContent,
4524    boolean bWithVersions, XWikiContext context) throws XWikiException
4525    {
4526    // Input
4527  6332 DocumentInstanceInputProperties documentProperties = new DocumentInstanceInputProperties();
4528  6332 documentProperties.setWithWikiObjects(bWithObjects);
4529  6332 documentProperties.setWithWikiDocumentContentHTML(bWithRendering);
4530  6332 documentProperties.setWithWikiAttachments(bWithAttachmentContent);
4531  6332 documentProperties.setWithJRCSRevisions(bWithVersions);
4532  6332 documentProperties.setWithRevisions(false);
4533   
4534    // Output
4535  6332 XAROutputProperties xarProperties = new XAROutputProperties();
4536  6332 xarProperties.setPreserveVersion(bWithVersions);
4537  6332 xarProperties.setEncoding(context.getWiki().getEncoding());
4538   
4539  6332 try {
4540  6332 Utils.getComponent(XWikiDocumentFilterUtils.class).exportEntity(this, out, xarProperties,
4541    documentProperties);
4542    } catch (Exception e) {
4543  0 throw new XWikiException(XWikiException.MODULE_XWIKI_DOC, XWikiException.ERROR_DOC_XML_PARSING,
4544    "Error parsing xml", e, null);
4545    }
4546    }
4547   
 
4548  0 toggle protected String encodedXMLStringAsUTF8(String xmlString)
4549    {
4550  0 if (xmlString == null) {
4551  0 return "";
4552    }
4553   
4554  0 int length = xmlString.length();
4555  0 char character;
4556  0 StringBuilder result = new StringBuilder();
4557  0 for (int i = 0; i < length; i++) {
4558  0 character = xmlString.charAt(i);
4559  0 switch (character) {
4560  0 case '&':
4561  0 result.append("&amp;");
4562  0 break;
4563  0 case '"':
4564  0 result.append("&quot;");
4565  0 break;
4566  0 case '<':
4567  0 result.append("&lt;");
4568  0 break;
4569  0 case '>':
4570  0 result.append("&gt;");
4571  0 break;
4572  0 case '\n':
4573  0 result.append("\n");
4574  0 break;
4575  0 case '\r':
4576  0 result.append("\r");
4577  0 break;
4578  0 case '\t':
4579  0 result.append("\t");
4580  0 break;
4581  0 default:
4582  0 if (character < 0x20) {
4583  0 } else if (character > 0x7F) {
4584  0 result.append("&#x");
4585  0 result.append(Integer.toHexString(character).toUpperCase());
4586  0 result.append(";");
4587    } else {
4588  0 result.append(character);
4589    }
4590  0 break;
4591    }
4592    }
4593   
4594  0 return result.toString();
4595    }
4596   
 
4597  0 toggle protected String getElement(Element docel, String name)
4598    {
4599  0 Element el = docel.element(name);
4600  0 if (el == null) {
4601  0 return "";
4602    } else {
4603  0 return el.getText();
4604    }
4605    }
4606   
 
4607  35 toggle public void fromXML(String xml) throws XWikiException
4608    {
4609  35 fromXML(xml, false);
4610    }
4611   
 
4612  0 toggle public void fromXML(InputStream is) throws XWikiException
4613    {
4614  0 fromXML(is, false);
4615    }
4616   
 
4617  2027 toggle public void fromXML(InputSource source, boolean withArchive) throws XWikiException
4618    {
4619    // Output
4620  2027 DocumentInstanceOutputProperties documentProperties = new DocumentInstanceOutputProperties();
4621  2027 documentProperties.setVersionPreserved(withArchive);
4622   
4623    // Input
4624  2027 XARInputProperties xarProperties = new XARInputProperties();
4625  2027 xarProperties.setWithHistory(withArchive);
4626   
4627  2027 try {
4628  2027 Utils.getComponent(XWikiDocumentFilterUtils.class).importEntity(XWikiDocument.class, this, source,
4629    xarProperties, documentProperties);
4630    } catch (Exception e) {
4631  0 throw new XWikiException(XWikiException.MODULE_XWIKI_DOC, XWikiException.ERROR_DOC_XML_PARSING,
4632    "Error parsing xml", e, null);
4633    }
4634    }
4635   
 
4636  39 toggle public void fromXML(String source, boolean withArchive) throws XWikiException
4637    {
4638  39 fromXML(new StringInputSource(source), withArchive);
4639    }
4640   
 
4641  1988 toggle public void fromXML(InputStream source, boolean withArchive) throws XWikiException
4642    {
4643  1988 fromXML(new DefaultInputStreamInputSource(source), withArchive);
4644    }
4645   
4646    /**
4647    * @deprecated since 9.0RC1, use {@link #fromXML(InputStream)} instead
4648    */
 
4649  1 toggle @Deprecated
4650    public void fromXML(Document domdoc, boolean withArchive) throws XWikiException
4651    {
4652    // Serialize the Document (could not find a way to convert a dom4j Document into a usable StAX source)
4653  1 StringWriter writer = new StringWriter();
4654  1 try {
4655  1 org.dom4j.io.XMLWriter domWriter = new org.dom4j.io.XMLWriter(writer);
4656  1 domWriter.write(domdoc);
4657  1 domWriter.flush();
4658    } catch (IOException e) {
4659  0 throw new XWikiException(XWikiException.MODULE_XWIKI_DOC, XWikiException.ERROR_DOC_XML_PARSING,
4660    "Error parsing xml", e, null);
4661    }
4662   
4663    // Actually parse the XML
4664  1 fromXML(writer.toString(), withArchive);
4665    }
4666   
4667    /**
4668    * Check if provided xml document is a wiki document.
4669    *
4670    * @param domdoc the xml document.
4671    * @return true if provided xml document is a wiki document.
4672    */
 
4673  0 toggle public static boolean containsXMLWikiDocument(Document domdoc)
4674    {
4675  0 return domdoc.getRootElement().getName().equals(XarDocumentModel.ELEMENT_DOCUMENT);
4676    }
4677   
 
4678  158 toggle public void setAttachmentList(List<XWikiAttachment> list)
4679    {
4680    // For backwards compatibility reasons (and in general), we need to allow callers to do something like
4681    // setAttachmentList(getAttachmentList())
4682  158 if (this.attachmentList != list) {
4683  156 this.attachmentList.clear();
4684  156 this.attachmentList.addAll(list);
4685    }
4686    }
4687   
 
4688  138688 toggle public List<XWikiAttachment> getAttachmentList()
4689    {
4690  138689 return this.attachmentList;
4691    }
4692   
 
4693  0 toggle public void saveAllAttachments(XWikiContext context) throws XWikiException
4694    {
4695  0 saveAllAttachments(true, true, context);
4696    }
4697   
 
4698  0 toggle public void saveAllAttachments(boolean updateParent, boolean transaction, XWikiContext context)
4699    throws XWikiException
4700    {
4701  0 for (XWikiAttachment attachment : this.attachmentList) {
4702  0 saveAttachmentContent(attachment, false, transaction, context);
4703    }
4704   
4705    // Save the document
4706  0 if (updateParent) {
4707  0 context.getWiki().saveDocument(this, context);
4708    }
4709    }
4710   
 
4711  0 toggle public void saveAttachmentsContent(List<XWikiAttachment> attachments, XWikiContext context) throws XWikiException
4712    {
4713  0 String database = context.getWikiId();
4714  0 try {
4715    // We might need to switch database to get the translated content
4716  0 if (getDatabase() != null) {
4717  0 context.setWikiId(getDatabase());
4718    }
4719   
4720  0 context.getWiki().getAttachmentStore().saveAttachmentsContent(attachments, this, true, context, true);
4721    } catch (OutOfMemoryError e) {
4722  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP, XWikiException.ERROR_XWIKI_APP_JAVA_HEAP_SPACE,
4723    "Out Of Memory Exception");
4724    } finally {
4725  0 if (database != null) {
4726  0 context.setWikiId(database);
4727    }
4728    }
4729    }
4730   
 
4731  0 toggle public void saveAttachmentContent(XWikiAttachment attachment, XWikiContext context) throws XWikiException
4732    {
4733  0 saveAttachmentContent(attachment, true, true, context);
4734    }
4735   
 
4736  0 toggle public void saveAttachmentContent(XWikiAttachment attachment, boolean updateParent, boolean transaction,
4737    XWikiContext context) throws XWikiException
4738    {
4739  0 String currentWiki = context.getWikiId();
4740  0 try {
4741    // We might need to switch database to
4742    // get the translated content
4743  0 if (getDatabase() != null) {
4744  0 context.setWikiId(getDatabase());
4745    }
4746   
4747    // Save the attachment
4748  0 context.getWiki().getAttachmentStore().saveAttachmentContent(attachment, false, context, transaction);
4749   
4750    // We need to make sure there is a version upgrade
4751  0 setMetaDataDirty(true);
4752   
4753    // Save the document
4754  0 if (updateParent) {
4755  0 context.getWiki().saveDocument(this, context);
4756    }
4757    } catch (OutOfMemoryError e) {
4758  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP, XWikiException.ERROR_XWIKI_APP_JAVA_HEAP_SPACE,
4759    "Out Of Memory Exception");
4760    } finally {
4761  0 if (currentWiki != null) {
4762  0 context.setWikiId(currentWiki);
4763    }
4764    }
4765    }
4766   
 
4767  280 toggle public void loadAttachmentContent(XWikiAttachment attachment, XWikiContext context) throws XWikiException
4768    {
4769  280 String database = context.getWikiId();
4770  280 try {
4771    // We might need to switch database to
4772    // get the translated content
4773  278 if (getDatabase() != null) {
4774  277 context.setWikiId(getDatabase());
4775    }
4776   
4777  279 context.getWiki().getAttachmentStore().loadAttachmentContent(attachment, context, true);
4778    } finally {
4779  280 if (database != null) {
4780  280 context.setWikiId(database);
4781    }
4782    }
4783    }
4784   
4785    /**
4786    * Remove the attachment from the document attachment list and put it in the list of attachments to remove for next
4787    * document save.
4788    * <p>
4789    * The attachment will be move to recycle bin.
4790    *
4791    * @param attachment the attachment to remove
4792    * @return the removed attachment, null if none could be found
4793    * @since 5.2M1
4794    */
 
4795  8 toggle public XWikiAttachment removeAttachment(XWikiAttachment attachment)
4796    {
4797  8 return removeAttachment(attachment, true);
4798    }
4799   
4800    /**
4801    * Remove the attachment from the document attachment list and put it in the list of attachments to remove for next
4802    * document save.
4803    *
4804    * @param attachmentToRemove the attachment to remove
4805    * @param toRecycleBin indicate if the attachment should be moved to recycle bin
4806    * @return the removed attachment, null if none could be found
4807    * @since 5.2M1
4808    */
 
4809  8 toggle public XWikiAttachment removeAttachment(XWikiAttachment attachmentToRemove, boolean toRecycleBin)
4810    {
4811  8 List<XWikiAttachment> list = getAttachmentList();
4812  8 for (int i = 0; i < list.size(); i++) {
4813  8 XWikiAttachment attachment = list.get(i);
4814  8 if (attachmentToRemove.getFilename().equals(attachment.getFilename())) {
4815  8 list.remove(i);
4816  8 this.attachmentsToRemove.add(new XWikiAttachmentToRemove(attachment, toRecycleBin));
4817  8 setMetaDataDirty(true);
4818  8 return attachment;
4819    }
4820    }
4821   
4822  0 return null;
4823    }
4824   
4825    /**
4826    * @return the attachment planned for removal
4827    */
 
4828  7707 toggle public List<XWikiAttachmentToRemove> getAttachmentsToRemove()
4829    {
4830  7707 return this.attachmentsToRemove;
4831    }
4832   
4833    /**
4834    * Clear the list of attachments planned for removal.
4835    */
 
4836  5 toggle public void clearAttachmentsToRemove()
4837    {
4838  5 this.attachmentsToRemove.clear();
4839    }
4840   
4841    /**
4842    * Get the wiki document references pointing to this document.
4843    * <p>
4844    * Theses links are stored to the database when documents are saved. You can use "backlinks" in XWikiPreferences or
4845    * "xwiki.backlinks" in xwiki.cfg file to enable links storage in the database.
4846    *
4847    * @param context the XWiki context.
4848    * @return the found wiki document references
4849    * @throws XWikiException error when getting pages names from database.
4850    * @since 2.2M2
4851    */
 
4852  5 toggle public List<DocumentReference> getBackLinkedReferences(XWikiContext context) throws XWikiException
4853    {
4854  5 return getStore(context).loadBacklinks(getDocumentReference(), true, context);
4855    }
4856   
4857    /**
4858    * @deprecated since 2.2M2 use {@link #getBackLinkedReferences(XWikiContext)}
4859    */
 
4860  14 toggle @Deprecated
4861    public List<String> getBackLinkedPages(XWikiContext context) throws XWikiException
4862    {
4863  14 return getStore(context).loadBacklinks(getFullName(), context, true);
4864    }
4865   
4866    /**
4867    * Get a list of unique links from this document to others documents.
4868    * <ul>
4869    * <li>xwiki/1.0 content: get the unique links associated to document from database. This is stored when the
4870    * document is saved. You can use "backlinks" in XWikiPreferences or "xwiki.backlinks" in xwiki.cfg file to enable
4871    * links storage in the database.</li>
4872    * <li>Other content: call {@link #getUniqueLinkedPages(XWikiContext)} and generate the List.</li>
4873    * </ul>
4874    *
4875    * @param context the XWiki context
4876    * @return the found wiki links.
4877    * @throws XWikiException error when getting links from database when xwiki/1.0 content.
4878    * @since 1.9M2
4879    */
 
4880  3805 toggle public Set<XWikiLink> getUniqueWikiLinkedPages(XWikiContext context) throws XWikiException
4881    {
4882  3805 Set<XWikiLink> links;
4883   
4884  3805 if (is10Syntax()) {
4885  3 links = new LinkedHashSet<>(getStore(context).loadLinks(getId(), context, true));
4886    } else {
4887  3802 Set<String> linkedPages = getUniqueLinkedPages(context);
4888  3802 links = new LinkedHashSet<>(linkedPages.size());
4889  3802 for (String linkedPage : linkedPages) {
4890  40 XWikiLink wikiLink = new XWikiLink();
4891   
4892  40 wikiLink.setDocId(getId());
4893  40 wikiLink.setFullName(LOCAL_REFERENCE_SERIALIZER.serialize(getDocumentReference()));
4894  40 wikiLink.setLink(linkedPage);
4895   
4896  40 links.add(wikiLink);
4897    }
4898    }
4899   
4900  3805 return links;
4901    }
4902   
4903    /**
4904    * Extract all the unique static (i.e. not generated by macros) wiki links (pointing to wiki page) from this
4905    * xwiki/1.0 document's content to others documents.
4906    *
4907    * @param context the XWiki context.
4908    * @return the document names for linked pages, if null an error append.
4909    * @since 1.9M2
4910    */
 
4911  1 toggle private Set<String> getUniqueLinkedPages10(XWikiContext context)
4912    {
4913  1 Set<String> pageNames;
4914   
4915  1 try {
4916  1 List<String> list = context.getUtil().getUniqueMatches(getContent(), "\\[(.*?)\\]", 1);
4917  1 pageNames = new HashSet<String>(list.size());
4918   
4919  1 DocumentReference currentDocumentReference = getDocumentReference();
4920  1 for (String name : list) {
4921  7 int i1 = name.indexOf('>');
4922  7 if (i1 != -1) {
4923  3 name = name.substring(i1 + 1);
4924    }
4925  7 i1 = name.indexOf("&gt;");
4926  7 if (i1 != -1) {
4927  0 name = name.substring(i1 + 4);
4928    }
4929  7 i1 = name.indexOf('#');
4930  7 if (i1 != -1) {
4931  1 name = name.substring(0, i1);
4932    }
4933  7 i1 = name.indexOf('?');
4934  7 if (i1 != -1) {
4935  1 name = name.substring(0, i1);
4936    }
4937   
4938    // Let's get rid of anything that's not a real link
4939  7 if (name.trim().equals("") || (name.indexOf('$') != -1) || (name.indexOf("://") != -1)
4940    || (name.indexOf('"') != -1) || (name.indexOf('\'') != -1) || (name.indexOf("..") != -1)
4941    || (name.indexOf(':') != -1) || (name.indexOf('=') != -1)) {
4942  3 continue;
4943    }
4944   
4945    // generate the link
4946  4 String newname = StringUtils.replace(Util.noaccents(name), " ", "");
4947   
4948    // If it is a local link let's add the space
4949  4 if (newname.indexOf('.') == -1) {
4950  2 newname = getSpace() + "." + name;
4951    }
4952  4 if (context.getWiki().exists(newname, context)) {
4953  4 name = newname;
4954    } else {
4955    // If it is a local link let's add the space
4956  0 if (name.indexOf('.') == -1) {
4957  0 name = getSpace() + "." + name;
4958    }
4959    }
4960   
4961    // If the reference is empty, the link is an autolink
4962  4 if (!StringUtils.isEmpty(name)) {
4963    // The reference may not have the space or even document specified (in case of an empty
4964    // string)
4965    // Thus we need to find the fully qualified document name
4966  4 DocumentReference documentReference = getCurrentDocumentReferenceResolver().resolve(name);
4967   
4968    // Verify that the link is not an autolink (i.e. a link to the current document)
4969  4 if (!documentReference.equals(currentDocumentReference)) {
4970  4 pageNames.add(getCompactEntityReferenceSerializer().serialize(documentReference));
4971    }
4972    }
4973    }
4974   
4975  1 return pageNames;
4976    } catch (Exception e) {
4977    // This should never happen
4978  0 LOGGER.error("Failed to get linked documents", e);
4979   
4980  0 return null;
4981    }
4982    }
4983   
4984    /**
4985    * Extract all the unique static (i.e. not generated by macros) wiki links (pointing to wiki page) from this
4986    * document's content to others documents.
4987    *
4988    * @param context the XWiki context.
4989    * @return the document names for linked pages, if null an error append.
4990    * @since 1.9M2
4991    */
 
4992  3804 toggle public Set<String> getUniqueLinkedPages(XWikiContext context)
4993    {
4994  3804 Set<String> pageNames;
4995   
4996  3804 XWikiDocument contextDoc = context.getDoc();
4997  3804 String contextWiki = context.getWikiId();
4998   
4999  3804 try {
5000    // Make sure the right document is used as context document
5001  3804 context.setDoc(this);
5002    // Make sure the right wiki is used as context document
5003  3804 context.setWikiId(getDatabase());
5004   
5005  3804 if (is10Syntax()) {
5006  1 pageNames = getUniqueLinkedPages10(context);
5007    } else {
5008  3803 XDOM dom = getXDOM();
5009   
5010    // TODO: Add support for macro as well.
5011  3803 List<LinkBlock> linkBlocks =
5012    dom.getBlocks(new ClassBlockMatcher(LinkBlock.class), Block.Axes.DESCENDANT);
5013  3803 pageNames = new LinkedHashSet<String>(linkBlocks.size());
5014   
5015  3803 DocumentReference currentDocumentReference = getDocumentReference();
5016   
5017  3803 for (LinkBlock linkBlock : linkBlocks) {
5018  66 ResourceReference reference = linkBlock.getReference();
5019  66 String referenceString = reference.getReference();
5020  66 ResourceType resourceType = reference.getType();
5021   
5022    // TODO: Add support for ATTACHMENT as well.
5023  66 if (!ResourceType.DOCUMENT.equals(resourceType) && !ResourceType.SPACE.equals(resourceType)) {
5024    // We are only interested in Document or Space references.
5025  18 continue;
5026    }
5027   
5028    // Optimisation: If the reference is empty, the link is an autolink and we don`t include it.
5029  48 if (StringUtils.isEmpty(referenceString)) {
5030  3 continue;
5031    }
5032   
5033    // FIXME?: pass this.getDocumentReference as parameter to the resolve so that relative references
5034    // get resolved relative to this and not to the context document.
5035  45 EntityReference documentReference =
5036    getResourceReferenceEntityReferenceResolver().resolve(reference, EntityType.DOCUMENT);
5037   
5038    // Verify after resolving it that the link is not an autolink(i.e. a link to the current document)
5039  45 if (!documentReference.equals(currentDocumentReference)) {
5040    // Since this method is used for saving backlinks and since backlinks must be
5041    // saved with the space and page name but without the wiki part, we remove the wiki
5042    // part before serializing.
5043    // This is a bit of a hack since the default serializer should theoretically fail
5044    // if it's passed an invalid reference.
5045  45 pageNames.add(getCompactWikiEntityReferenceSerializer().serialize(documentReference));
5046    }
5047    }
5048    }
5049    } finally {
5050  3804 context.setDoc(contextDoc);
5051  3804 context.setWikiId(contextWiki);
5052    }
5053   
5054  3804 return pageNames;
5055    }
5056   
5057    /**
5058    * Returns a list of references of all documents which list this document as their parent, in the current wiki.
5059    * {@link #getChildren(int, int, com.xpn.xwiki.XWikiContext)}
5060    *
5061    * @since 2.2M2
5062    */
 
5063  5 toggle public List<DocumentReference> getChildrenReferences(XWikiContext context) throws XWikiException
5064    {
5065  5 return getChildrenReferences(0, 0, context);
5066    }
5067   
5068    /**
5069    * @deprecated since 2.2M2 use {@link #getChildrenReferences(XWikiContext)}
5070    */
 
5071  554 toggle @Deprecated
5072    public List<String> getChildren(XWikiContext context) throws XWikiException
5073    {
5074  554 return getChildren(0, 0, context);
5075    }
5076   
5077    /**
5078    * Returns a list of references of all documents which list this document as their parent, in the current wiki.
5079    *
5080    * @param nb The number of results to return.
5081    * @param start The number of results to skip before we begin returning results.
5082    * @param context The {@link com.xpn.xwiki.XWikiContext context}.
5083    * @return the list of document references
5084    * @throws XWikiException If there's an error querying the database.
5085    * @since 2.2M2
5086    */
 
5087  560 toggle public List<DocumentReference> getChildrenReferences(int nb, int start, XWikiContext context) throws XWikiException
5088    {
5089    // Use cases:
5090    // - the parent document reference saved in the database matches the reference of this document, in its fully
5091    // serialized form (eg "wiki:space.page"). Note that this is normally not required since the wiki part
5092    // isn't saved in the database when it matches the current wiki.
5093    // - the parent document reference saved in the database matches the reference of this document, in its
5094    // serialized form without the wiki part (eg "space.page"). The reason we don't need to specify the wiki
5095    // part is because document parents saved in the database don't have the wiki part specified when it matches
5096    // the current wiki.
5097    // - the parent document reference saved in the database matches the page name part of this document's
5098    // reference (eg "page") and the parent document's space is the same as this document's space.
5099  560 List<DocumentReference> children = new ArrayList<DocumentReference>();
5100   
5101  560 try {
5102  560 Query query = getStore().getQueryManager().createQuery(
5103    "select distinct doc.fullName from XWikiDocument doc where "
5104    + "doc.parent=:prefixedFullName or doc.parent=:fullName or (doc.parent=:name and doc.space=:space)",
5105    Query.XWQL);
5106  560 query.addFilter(Utils.getComponent(QueryFilter.class, "hidden"));
5107  560 query.bindValue("prefixedFullName",
5108    getDefaultEntityReferenceSerializer().serialize(getDocumentReference()));
5109  560 query.bindValue("fullName", LOCAL_REFERENCE_SERIALIZER.serialize(getDocumentReference()));
5110  560 query.bindValue("name", getDocumentReference().getName());
5111  560 query.bindValue("space",
5112    LOCAL_REFERENCE_SERIALIZER.serialize(getDocumentReference().getLastSpaceReference()));
5113  560 query.setLimit(nb).setOffset(start);
5114   
5115  560 List<String> queryResults = query.execute();
5116  560 WikiReference wikiReference = this.getDocumentReference().getWikiReference();
5117  560 for (String fullName : queryResults) {
5118  28 children.add(getCurrentDocumentReferenceResolver().resolve(fullName, wikiReference));
5119    }
5120    } catch (QueryException e) {
5121  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_UNKNOWN,
5122    String.format("Failed to retrieve children for document [%s]", this.getDocumentReference()), e);
5123    }
5124   
5125  560 return children;
5126    }
5127   
5128    /**
5129    * @deprecated since 2.2M2 use {@link #getChildrenReferences(XWikiContext)}
5130    */
 
5131  554 toggle @Deprecated
5132    public List<String> getChildren(int nb, int start, XWikiContext context) throws XWikiException
5133    {
5134  554 List<String> childrenNames = new ArrayList<String>();
5135  554 for (DocumentReference reference : getChildrenReferences(nb, start, context)) {
5136  26 childrenNames.add(LOCAL_REFERENCE_SERIALIZER.serialize(reference));
5137    }
5138  554 return childrenNames;
5139    }
5140   
5141    /**
5142    * @since 2.2M2
5143    */
 
5144  2 toggle public void renameProperties(DocumentReference classReference, Map<String, String> fieldsToRename)
5145    {
5146  2 List<BaseObject> objects = this.xObjects.get(classReference);
5147  2 if (objects == null) {
5148  2 return;
5149    }
5150   
5151  0 boolean isDirty = false;
5152  0 for (BaseObject bobject : objects) {
5153  0 if (bobject == null) {
5154  0 continue;
5155    }
5156  0 for (Map.Entry<String, String> entry : fieldsToRename.entrySet()) {
5157  0 String origname = entry.getKey();
5158  0 String newname = entry.getValue();
5159  0 BaseProperty origprop = (BaseProperty) bobject.safeget(origname);
5160  0 if (origprop != null) {
5161  0 BaseProperty prop = origprop.clone();
5162  0 bobject.removeField(origname);
5163  0 prop.setName(newname);
5164  0 bobject.addField(newname, prop);
5165   
5166  0 isDirty = true;
5167    }
5168    }
5169    }
5170   
5171    // If at least one property was renamed, mark the document dirty.
5172  0 if (isDirty) {
5173  0 setMetaDataDirty(true);
5174    }
5175    }
5176   
5177    /**
5178    * @deprecated since 2.2M2 use {@link #renameProperties(DocumentReference, Map)} instead
5179    */
 
5180  2 toggle @Deprecated
5181    public void renameProperties(String className, Map<String, String> fieldsToRename)
5182    {
5183  2 renameProperties(resolveClassReference(className), fieldsToRename);
5184    }
5185   
5186    /**
5187    * @since 2.2M1
5188    */
 
5189  115 toggle public void addXObjectToRemove(BaseObject object)
5190    {
5191  115 getXObjectsToRemove().add(object);
5192  115 object.setOwnerDocument(null);
5193  115 setMetaDataDirty(true);
5194    }
5195   
5196    /**
5197    * Automatically add objects present in the old version, but not in the current document, to the list of objects
5198    * marked for removal from the database.
5199    *
5200    * @param previousVersion the version of the document present in the database
5201    * @since 3.3M2
5202    */
 
5203  5 toggle public void addXObjectsToRemoveFromVersion(XWikiDocument previousVersion)
5204    {
5205  5 if (previousVersion == null) {
5206  0 return;
5207    }
5208  5 for (List<BaseObject> objects : previousVersion.getXObjects().values()) {
5209  4 for (BaseObject originalObj : objects) {
5210  8 if (originalObj != null) {
5211  8 BaseObject newObj = getXObject(originalObj.getXClassReference(), originalObj.getNumber());
5212  8 if (newObj == null) {
5213    // The object was deleted.
5214  2 this.addXObjectToRemove(originalObj);
5215    }
5216    }
5217    }
5218    }
5219    }
5220   
5221    /**
5222    * @deprecated since 2.2M2 use {@link #addXObjectToRemove(BaseObject)} )} instead
5223    */
 
5224  0 toggle @Deprecated
5225    public void addObjectsToRemove(BaseObject object)
5226    {
5227  0 addXObjectToRemove(object);
5228    }
5229   
5230    /**
5231    * @since 2.2M2
5232    */
 
5233  4124 toggle public List<BaseObject> getXObjectsToRemove()
5234    {
5235  4124 return this.xObjectsToRemove;
5236    }
5237   
5238    /**
5239    * @deprecated since 2.2M2 use {@link #getObjectsToRemove()} instead
5240    */
 
5241  0 toggle @Deprecated
5242    public ArrayList<BaseObject> getObjectsToRemove()
5243    {
5244  0 return (ArrayList<BaseObject>) getXObjectsToRemove();
5245    }
5246   
5247    /**
5248    * @since 2.2M1
5249    */
 
5250  51 toggle public void setXObjectsToRemove(List<BaseObject> objectsToRemove)
5251    {
5252  51 this.xObjectsToRemove = objectsToRemove;
5253  51 setMetaDataDirty(true);
5254    }
5255   
 
5256  3813 toggle public List<String> getIncludedPages(XWikiContext context)
5257    {
5258  3813 try {
5259  3813 return getIncludedPagesInternal(context);
5260    } catch (Exception e) {
5261    // If an error happens then we return an empty list of included pages. We don't want to fail by throwing an
5262    // exception since it'll lead to several errors in the UI (such as in the Information Panel in edit mode).
5263  0 LOGGER.error("Failed to get included pages for [{}]", getDocumentReference(), e);
5264  0 return Collections.emptyList();
5265    }
5266    }
5267   
 
5268  3813 toggle private List<String> getIncludedPagesInternal(XWikiContext context)
5269    {
5270  3813 if (is10Syntax()) {
5271  3 return getIncludedPagesForXWiki10Syntax(getContent(), context);
5272    } else {
5273    // Find all include macros listed on the page
5274  3810 XDOM dom = getXDOM();
5275   
5276  3810 List<String> result = new ArrayList<String>();
5277  3810 List<MacroBlock> macroBlocks =
5278    dom.getBlocks(new ClassBlockMatcher(MacroBlock.class), Block.Axes.DESCENDANT);
5279  3810 for (MacroBlock macroBlock : macroBlocks) {
5280    // - Add each document pointed to by the include macro
5281    // - Also add all the included pages found in the velocity macro when using the deprecated #include*
5282    // macros
5283    // This should be removed when we fully drop support for the XWiki Syntax 1.0 but for now we want to
5284    // play nice with people migrating from 1.0 to 2.0 syntax
5285   
5286  1883 if (macroBlock.getId().equalsIgnoreCase("include") || macroBlock.getId().equalsIgnoreCase("display")) {
5287  455 String documentName = macroBlock.getParameters().get("reference");
5288  455 if (StringUtils.isEmpty(documentName)) {
5289  5 documentName = macroBlock.getParameters().get("document");
5290  5 if (StringUtils.isEmpty(documentName)) {
5291  4 continue;
5292    }
5293    }
5294   
5295  451 DocumentReference documentReference =
5296    getExplicitDocumentReferenceResolver().resolve(documentName, getDocumentReference());
5297  451 if (this.getDocumentReference().equals(documentReference)) {
5298    // Skip auto-includes since they are not allowed anyway.
5299  2 continue;
5300    }
5301   
5302  449 documentName = LOCAL_REFERENCE_SERIALIZER.serialize(documentReference);
5303   
5304  449 result.add(documentName);
5305  1428 } else if (macroBlock.getId().equalsIgnoreCase("velocity")
5306    && !StringUtils.isEmpty(macroBlock.getContent())) {
5307    // Try to find matching content inside each velocity macro
5308  980 result.addAll(getIncludedPagesForXWiki10Syntax(macroBlock.getContent(), context));
5309    }
5310    }
5311   
5312  3810 return result;
5313    }
5314    }
5315   
 
5316  983 toggle private List<String> getIncludedPagesForXWiki10Syntax(String content, XWikiContext context)
5317    {
5318  983 try {
5319  983 String pattern = "#include(Topic|InContext|Form|Macros|parseGroovyFromPage)\\([\"'](.*?)[\"']\\)";
5320  983 List<String> list = context.getUtil().getUniqueMatches(content, pattern, 2);
5321  986 for (int i = 0; i < list.size(); i++) {
5322  3 String name = list.get(i);
5323  3 if (name.indexOf('.') == -1) {
5324  0 list.set(i, getSpace() + "." + name);
5325    }
5326    }
5327   
5328  983 return list;
5329    } catch (Exception e) {
5330  0 LOGGER.error("Failed to extract include target from provided content [" + content + "]", e);
5331   
5332  0 return null;
5333    }
5334    }
5335   
 
5336  0 toggle public List<String> getIncludedMacros(XWikiContext context)
5337    {
5338  0 return context.getWiki().getIncludedMacros(getSpace(), getContent(), context);
5339    }
5340   
 
5341  0 toggle public String displayRendered(PropertyClass pclass, String prefix, BaseCollection object, XWikiContext context)
5342    throws XWikiException
5343    {
5344  0 String result = pclass.displayView(pclass.getName(), prefix, object, context);
5345  0 return getRenderedContent(result, Syntax.XWIKI_1_0.toIdString(), context);
5346    }
5347   
 
5348  326 toggle public String displayView(PropertyClass pclass, String prefix, BaseCollection object, XWikiContext context)
5349    {
5350  326 return (pclass == null) ? "" : pclass.displayView(pclass.getName(), prefix, object, context);
5351    }
5352   
 
5353  2034 toggle public String displayEdit(PropertyClass pclass, String prefix, BaseCollection object, XWikiContext context)
5354    {
5355  2034 return (pclass == null) ? "" : pclass.displayEdit(pclass.getName(), prefix, object, context);
5356    }
5357   
 
5358  0 toggle public String displayHidden(PropertyClass pclass, String prefix, BaseCollection object, XWikiContext context)
5359    {
5360  0 return (pclass == null) ? "" : pclass.displayHidden(pclass.getName(), prefix, object, context);
5361    }
5362   
5363    /**
5364    * @param filename the file name of the attachment with or without the extension
5365    * @return the {@link XWikiAttachment} corresponding to the file name, null if none can be found
5366    */
 
5367  5153 toggle public XWikiAttachment getAttachment(String filename)
5368    {
5369  5153 for (XWikiAttachment attach : getAttachmentList()) {
5370  30355 if (attach.getFilename().equals(filename)) {
5371  4417 return attach;
5372    }
5373    }
5374   
5375  736 for (XWikiAttachment attach : getAttachmentList()) {
5376  110 if (attach.getFilename().startsWith(filename + ".")) {
5377  0 return attach;
5378    }
5379    }
5380   
5381  736 return null;
5382    }
5383   
5384    /**
5385    * Add passed attachment to the document.
5386    *
5387    * @param attachment the attachment to add
5388    * @since 5.3M2
5389    */
 
5390  472 toggle public void addAttachment(XWikiAttachment attachment)
5391    {
5392  472 attachment.setDoc(this);
5393  472 getAttachmentList().add(attachment);
5394    }
5395   
5396    /**
5397    * @deprecated use {@link #addAttachment(String, InputStream, XWikiContext)} instead
5398    */
 
5399  5 toggle @Deprecated
5400    public XWikiAttachment addAttachment(String fileName, byte[] content, XWikiContext context) throws XWikiException
5401    {
5402  5 try {
5403  5 return addAttachment(fileName, new ByteArrayInputStream(content != null ? content : new byte[0]), context);
5404    } catch (IOException e) {
5405  0 throw new XWikiException(XWikiException.MODULE_XWIKI_DOC, XWikiException.ERROR_XWIKI_UNKNOWN,
5406    "Failed to set Attachment content", e);
5407    }
5408    }
5409   
 
5410  16 toggle public XWikiAttachment addAttachment(String fileName, InputStream content, XWikiContext context)
5411    throws XWikiException, IOException
5412    {
5413  16 int i = fileName.indexOf('\\');
5414  16 if (i == -1) {
5415  16 i = fileName.indexOf('/');
5416    }
5417   
5418  16 String filename = fileName.substring(i + 1);
5419   
5420  16 XWikiAttachment attachment = getAttachment(filename);
5421  16 if (attachment == null) {
5422  12 attachment = new XWikiAttachment(this, filename);
5423   
5424    // Add the attachment in the current doc
5425  12 getAttachmentList().add(attachment);
5426    }
5427   
5428  16 attachment.setContent(content);
5429  16 attachment.setAuthorReference(context.getUserReference());
5430   
5431  16 return attachment;
5432    }
5433   
 
5434  0 toggle public BaseObject getFirstObject(String fieldname)
5435    {
5436    // Keeping this function with context null for compatibility reasons.
5437    // It should not be used, since it would miss properties which are only defined in the class
5438    // and not present in the object because the object was not updated
5439  0 return getFirstObject(fieldname, null);
5440    }
5441   
 
5442  669 toggle public BaseObject getFirstObject(String fieldname, XWikiContext context)
5443    {
5444  669 Collection<List<BaseObject>> objectscoll = getXObjects().values();
5445  668 if (objectscoll == null) {
5446  0 return null;
5447    }
5448   
5449  667 for (List<BaseObject> objects : objectscoll) {
5450  1411 for (BaseObject obj : objects) {
5451  1411 if (obj != null) {
5452  1411 BaseClass bclass = obj.getXClass(context);
5453  1410 if (bclass != null) {
5454  1411 Set<String> set = bclass.getPropertyList();
5455  1411 if ((set != null) && set.contains(fieldname)) {
5456  667 return obj;
5457    }
5458    }
5459  744 Set<String> set = obj.getPropertyList();
5460  744 if ((set != null) && set.contains(fieldname)) {
5461  0 return obj;
5462    }
5463    }
5464    }
5465    }
5466   
5467  2 return null;
5468    }
5469   
5470    /**
5471    * @since 2.2.3
5472    */
 
5473  0 toggle public void setProperty(EntityReference classReference, String fieldName, BaseProperty value)
5474    {
5475  0 BaseObject bobject = prepareXObject(classReference);
5476  0 bobject.safeput(fieldName, value);
5477    }
5478   
5479    /**
5480    * @deprecated since 2.2M2 use {@link #setProperty(EntityReference, String, BaseProperty)} instead
5481    */
 
5482  0 toggle @Deprecated
5483    public void setProperty(String className, String fieldName, BaseProperty value)
5484    {
5485  0 setProperty(getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()),
5486    fieldName, value);
5487    }
5488   
5489    /**
5490    * @since 2.2M2
5491    */
 
5492  29 toggle public int getIntValue(DocumentReference classReference, String fieldName)
5493    {
5494  29 BaseObject obj = getXObject(classReference, 0);
5495  29 if (obj == null) {
5496  12 return 0;
5497    }
5498   
5499  17 return obj.getIntValue(fieldName);
5500    }
5501   
5502    /**
5503    * @deprecated since 2.2M2 use {@link #getIntValue(DocumentReference, String)} instead
5504    */
 
5505  0 toggle @Deprecated
5506    public int getIntValue(String className, String fieldName)
5507    {
5508  0 return getIntValue(resolveClassReference(className), fieldName);
5509    }
5510   
5511    /**
5512    * @since 2.2M2
5513    */
 
5514  0 toggle public long getLongValue(DocumentReference classReference, String fieldName)
5515    {
5516  0 BaseObject obj = getXObject(classReference, 0);
5517  0 if (obj == null) {
5518  0 return 0;
5519    }
5520   
5521  0 return obj.getLongValue(fieldName);
5522    }
5523   
5524    /**
5525    * @deprecated since 2.2M2 use {@link #getLongValue(DocumentReference, String)} instead
5526    */
 
5527  0 toggle @Deprecated
5528    public long getLongValue(String className, String fieldName)
5529    {
5530  0 return getLongValue(resolveClassReference(className), fieldName);
5531    }
5532   
5533    /**
5534    * @since 6.2M1
5535    */
 
5536  0 toggle public String getStringValue(EntityReference classReference, String fieldName)
5537    {
5538  0 return getStringValue(resolveClassReference(classReference), fieldName);
5539    }
5540   
5541    /**
5542    * @since 2.2M2
5543    */
 
5544  586 toggle public String getStringValue(DocumentReference classReference, String fieldName)
5545    {
5546  586 BaseObject obj = getXObject(classReference);
5547  586 if (obj == null) {
5548  3 return "";
5549    }
5550   
5551  583 String result = obj.getStringValue(fieldName);
5552  583 if (result.equals(" ")) {
5553  0 return "";
5554    } else {
5555  581 return result;
5556    }
5557    }
5558   
5559    /**
5560    * @deprecated since 2.2M2 use {@link #getStringValue(DocumentReference, String)} instead
5561    */
 
5562  3 toggle @Deprecated
5563    public String getStringValue(String className, String fieldName)
5564    {
5565  3 return getStringValue(resolveClassReference(className), fieldName);
5566    }
5567   
 
5568  0 toggle public int getIntValue(String fieldName)
5569    {
5570  0 BaseObject object = getFirstObject(fieldName, null);
5571  0 if (object == null) {
5572  0 return 0;
5573    } else {
5574  0 return object.getIntValue(fieldName);
5575    }
5576    }
5577   
 
5578  0 toggle public long getLongValue(String fieldName)
5579    {
5580  0 BaseObject object = getFirstObject(fieldName, null);
5581  0 if (object == null) {
5582  0 return 0;
5583    } else {
5584  0 return object.getLongValue(fieldName);
5585    }
5586    }
5587   
 
5588  0 toggle public String getStringValue(String fieldName)
5589    {
5590  0 BaseObject object = getFirstObject(fieldName, null);
5591  0 if (object == null) {
5592  0 return "";
5593    }
5594   
5595  0 String result = object.getStringValue(fieldName);
5596  0 if (result.equals(" ")) {
5597  0 return "";
5598    } else {
5599  0 return result;
5600    }
5601    }
5602   
5603    /**
5604    * @since 2.2.3
5605    */
 
5606  20 toggle public void setStringValue(EntityReference classReference, String fieldName, String value)
5607    {
5608  20 BaseObject bobject = prepareXObject(classReference);
5609  20 bobject.setStringValue(fieldName, value);
5610    }
5611   
5612    /**
5613    * @deprecated since 2.2M2 use {@link #setStringValue(EntityReference, String, String)} instead
5614    */
 
5615  19 toggle @Deprecated
5616    public void setStringValue(String className, String fieldName, String value)
5617    {
5618  19 setStringValue(
5619    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()),
5620    fieldName, value);
5621    }
5622   
5623    /**
5624    * @since 2.2M2
5625    */
 
5626  0 toggle public List getListValue(DocumentReference classReference, String fieldName)
5627    {
5628  0 BaseObject obj = getXObject(classReference);
5629  0 if (obj == null) {
5630  0 return new ArrayList();
5631    }
5632   
5633  0 return obj.getListValue(fieldName);
5634    }
5635   
5636    /**
5637    * @deprecated since 2.2M2 use {@link #getListValue(DocumentReference, String)} instead
5638    */
 
5639  0 toggle @Deprecated
5640    public List getListValue(String className, String fieldName)
5641    {
5642  0 return getListValue(resolveClassReference(className), fieldName);
5643    }
5644   
 
5645  0 toggle public List getListValue(String fieldName)
5646    {
5647  0 BaseObject object = getFirstObject(fieldName, null);
5648  0 if (object == null) {
5649  0 return new ArrayList();
5650    }
5651   
5652  0 return object.getListValue(fieldName);
5653    }
5654   
5655    /**
5656    * @since 2.2.3
5657    */
 
5658  8 toggle public void setStringListValue(EntityReference classReference, String fieldName, List value)
5659    {
5660  8 BaseObject bobject = prepareXObject(classReference);
5661  8 bobject.setStringListValue(fieldName, value);
5662    }
5663   
5664    /**
5665    * @deprecated since 2.2M2 use {@link #setStringListValue(EntityReference, String, List)} instead
5666    */
 
5667  8 toggle @Deprecated
5668    public void setStringListValue(String className, String fieldName, List value)
5669    {
5670  8 setStringListValue(
5671    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()),
5672    fieldName, value);
5673    }
5674   
5675    /**
5676    * @since 2.2.3
5677    */
 
5678  20 toggle public void setDBStringListValue(EntityReference classReference, String fieldName, List value)
5679    {
5680  20 BaseObject bobject = prepareXObject(classReference);
5681  20 bobject.setDBStringListValue(fieldName, value);
5682    }
5683   
5684    /**
5685    * @deprecated since 2.2M2 use {@link #setDBStringListValue(EntityReference, String, List)} instead
5686    */
 
5687  20 toggle @Deprecated
5688    public void setDBStringListValue(String className, String fieldName, List value)
5689    {
5690  20 setDBStringListValue(
5691    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()),
5692    fieldName, value);
5693    }
5694   
5695    /**
5696    * @since 2.2.3
5697    */
 
5698  0 toggle public void setLargeStringValue(EntityReference classReference, String fieldName, String value)
5699    {
5700  0 BaseObject bobject = prepareXObject(classReference);
5701  0 bobject.setLargeStringValue(fieldName, value);
5702    }
5703   
5704    /**
5705    * @deprecated since 2.2M2 use {@link #setLargeStringValue(EntityReference, String, String)} instead
5706    */
 
5707  0 toggle @Deprecated
5708    public void setLargeStringValue(String className, String fieldName, String value)
5709    {
5710  0 setLargeStringValue(
5711    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()),
5712    fieldName, value);
5713    }
5714   
5715    /**
5716    * @since 2.2.3
5717    */
 
5718  0 toggle public void setIntValue(EntityReference classReference, String fieldName, int value)
5719    {
5720  0 BaseObject bobject = prepareXObject(classReference);
5721  0 bobject.setIntValue(fieldName, value);
5722    }
5723   
5724    /**
5725    * @deprecated since 2.2M2 use {@link #setIntValue(EntityReference, String, int)} instead
5726    */
 
5727  0 toggle @Deprecated
5728    public void setIntValue(String className, String fieldName, int value)
5729    {
5730  0 setIntValue(getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()),
5731    fieldName, value);
5732    }
5733   
5734    /**
5735    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
5736    *
5737    * @deprecated since 2.2M1 use {@link #getDocumentReference()} instead
5738    */
 
5739  61975 toggle @Deprecated
5740    public String getDatabase()
5741    {
5742  61973 return getDocumentReference().getWikiReference().getName();
5743    }
5744   
5745    /**
5746    * Note that this method cannot be removed for now since it's used by Hibernate for loading a XWikiDocument.
5747    *
5748    * @deprecated since 2.2M1 use {@link #setDocumentReference(DocumentReference)} instead
5749    */
 
5750  3836 toggle @Deprecated
5751    public void setDatabase(String database)
5752    {
5753  3836 if (database != null) {
5754  3836 DocumentReference reference = getDocumentReference();
5755  3836 WikiReference wiki = reference.getWikiReference();
5756  3836 WikiReference newWiki = new WikiReference(database);
5757  3836 if (!newWiki.equals(wiki)) {
5758  1 setDocumentReferenceInternal(reference.replaceParent(wiki, newWiki));
5759    }
5760    }
5761    }
5762   
5763    /**
5764    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
5765    *
5766    * @deprecated since 4.3M2 use {@link #getLocale()} instead
5767    */
 
5768  21679 toggle @Deprecated
5769    public String getLanguage()
5770    {
5771  21680 return getLocale().toString();
5772    }
5773   
5774    /**
5775    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
5776    *
5777    * @deprecated since 4.3M2 use {@link #setLocale(Locale)} instead
5778    */
 
5779  4113 toggle @Deprecated
5780    public void setLanguage(String language)
5781    {
5782  4115 setLocale(LocaleUtils.toLocale(Util.normalizeLanguage(language), Locale.ROOT));
5783    }
5784   
5785    /**
5786    * @return the locale of the document
5787    */
 
5788  1247687 toggle public Locale getLocale()
5789    {
5790  1247668 return this.locale != null ? this.locale : Locale.ROOT;
5791    }
5792   
5793    /**
5794    * @param locale the locale of the document
5795    */
 
5796  42627 toggle public void setLocale(Locale locale)
5797    {
5798  42631 this.locale = locale;
5799   
5800  42631 setMetaDataDirty(true);
5801   
5802    // Clean various caches
5803   
5804  42632 this.keyCache = null;
5805  42630 this.localKeyCache = null;
5806    }
5807   
5808    /**
5809    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
5810    *
5811    * @deprecated since 4.3M2 use {@link #getDefaultLocale()} instead
5812    */
 
5813  8792 toggle @Deprecated
5814    public String getDefaultLanguage()
5815    {
5816  8792 return getDefaultLocale().toString();
5817    }
5818   
5819    /**
5820    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
5821    *
5822    * @deprecated since 4.3M2 use {@link #setDefaultLocale(Locale)} instead
5823    */
 
5824  3856 toggle @Deprecated
5825    public void setDefaultLanguage(String defaultLanguage)
5826    {
5827  3856 setDefaultLocale(LocaleUtils.toLocale(defaultLanguage, Locale.ROOT));
5828    }
5829   
 
5830  117603 toggle public Locale getDefaultLocale()
5831    {
5832  117589 return this.defaultLocale != null ? this.defaultLocale : Locale.ROOT;
5833    }
5834   
 
5835  42971 toggle public void setDefaultLocale(Locale defaultLocale)
5836    {
5837  42977 this.defaultLocale = defaultLocale;
5838   
5839  42977 setMetaDataDirty(true);
5840    }
5841   
 
5842  59874 toggle public int getTranslation()
5843    {
5844  59880 return getLocale().equals(Locale.ROOT) ? 0 : 1;
5845    }
5846   
5847    /**
5848    * Note that this method cannot be removed for now since it's called by Hibernate when loading a XWikiDocument.
5849    *
5850    * @deprecated since 5.4.6, stored in the database to speedup some queries (really ?) but in {@link XWikiDocument}
5851    * it's calculated based on the document locale
5852    */
 
5853  3644 toggle @Deprecated
5854    public void setTranslation(int translation)
5855    {
5856    // Do nothing
5857    }
5858   
 
5859  158 toggle public String getTranslatedContent(XWikiContext context) throws XWikiException
5860    {
5861  158 String language = context.getWiki().getLanguagePreference(context);
5862   
5863  158 return getTranslatedContent(language, context);
5864    }
5865   
 
5866  158 toggle public String getTranslatedContent(String locale, XWikiContext context) throws XWikiException
5867    {
5868  158 XWikiDocument tdoc = getTranslatedDocument(locale, context);
5869  158 return tdoc.getContent();
5870    }
5871   
 
5872  59387 toggle public XWikiDocument getTranslatedDocument(XWikiContext context) throws XWikiException
5873    {
5874  59396 String locale = context.getWiki().getLanguagePreference(context);
5875  59385 return getTranslatedDocument(locale, context);
5876    }
5877   
5878    /**
5879    * Return the document in the provided language.
5880    * <p>
5881    * This method return this if the provided language does not exists. See
5882    *
5883    * @param language the language of the document to return
5884    * @param context the XWiki Context
5885    * @return the document in the provided language or this if the provided language does not exists
5886    * @throws XWikiException error when loading the document
5887    * @deprecated since 4.3M2 use {@link #getTranslatedDocument(Locale, XWikiContext)} insead
5888    */
 
5889  59591 toggle @Deprecated
5890    public XWikiDocument getTranslatedDocument(String language, XWikiContext context) throws XWikiException
5891    {
5892  59618 return getTranslatedDocument(LocaleUtils.toLocale(language, Locale.ROOT), context);
5893    }
5894   
5895    /**
5896    * Return the document in the provided language.
5897    * <p>
5898    * This method return this if the provided language does not exists. See
5899    *
5900    * @param locale the locale of the document to return
5901    * @param context the XWiki Context
5902    * @return the document in the provided language or this if the provided language does not exists
5903    * @throws XWikiException error when loading the document
5904    */
 
5905  59749 toggle public XWikiDocument getTranslatedDocument(Locale locale, XWikiContext context) throws XWikiException
5906    {
5907  59747 XWikiDocument tdoc = this;
5908   
5909  59745 if (locale != null && !locale.equals(Locale.ROOT) && !locale.equals(getDefaultLocale())) {
5910  50626 try {
5911  50625 tdoc = context.getWiki().getDocument(new DocumentReference(getDocumentReference(), locale), context);
5912   
5913  50644 if (tdoc.isNew()) {
5914  50462 tdoc = this;
5915    }
5916    } catch (Exception e) {
5917  0 tdoc = this;
5918    }
5919    }
5920   
5921  59762 return tdoc;
5922    }
5923   
5924    /**
5925    * @deprecated since 4.3M1 use {@link #getRealLocale()} instead
5926    */
 
5927  22 toggle @Deprecated
5928    public String getRealLanguage(XWikiContext context) throws XWikiException
5929    {
5930  22 return getRealLanguage();
5931    }
5932   
5933    /**
5934    * {@inheritDoc}
5935    *
5936    * @see org.xwiki.bridge.DocumentModelBridge#getRealLanguage()
5937    * @deprecated since 4.3M1 use {@link #getRealLocale()} instead
5938    */
 
5939  374 toggle @Override
5940    @Deprecated
5941    public String getRealLanguage()
5942    {
5943  374 String lang = getLanguage();
5944  374 if (lang.equals("")) {
5945  368 return getDefaultLanguage();
5946    } else {
5947  6 return lang;
5948    }
5949    }
5950   
5951    /**
5952    * @return the actual locale of the document
5953    */
 
5954  11094 toggle public Locale getRealLocale()
5955    {
5956  11094 Locale locale = getLocale();
5957  11094 if (locale.equals(Locale.ROOT)) {
5958  10976 locale = getDefaultLocale();
5959    }
5960   
5961  11094 return locale;
5962    }
5963   
5964    /**
5965    * @deprecated since 5.1M2 use {@link #getTranslationLocales(XWikiContext)} instead
5966    */
 
5967  5333 toggle @Deprecated
5968    public List<String> getTranslationList(XWikiContext context) throws XWikiException
5969    {
5970  5333 return getStore().getTranslationList(this, context);
5971    }
5972   
5973    /**
5974    * The locales of the translation of this document (the default locale is not included).
5975    *
5976    * @param context the XWiki context
5977    * @return the locales of the translations
5978    * @throws XWikiException if retriving the translations from the database failed
5979    */
 
5980  4511 toggle public List<Locale> getTranslationLocales(XWikiContext context) throws XWikiException
5981    {
5982  4511 List<String> translations = getTranslationList(context);
5983   
5984  4510 List<Locale> locales = new ArrayList<Locale>(translations.size());
5985  4510 for (String translationString : translations) {
5986  863 locales.add(LocaleUtils.toLocale(translationString));
5987    }
5988   
5989  4511 return locales;
5990    }
5991   
 
5992  0 toggle public List<Delta> getXMLDiff(XWikiDocument fromDoc, XWikiDocument toDoc, XWikiContext context)
5993    throws XWikiException, DifferentiationFailedException
5994    {
5995  0 return getDeltas(
5996    Diff.diff(ToString.stringToArray(fromDoc.toXML(context)), ToString.stringToArray(toDoc.toXML(context))));
5997    }
5998   
 
5999  1 toggle public List<Delta> getContentDiff(XWikiDocument fromDoc, XWikiDocument toDoc, XWikiContext context)
6000    throws XWikiException, DifferentiationFailedException
6001    {
6002  1 return getDeltas(
6003    Diff.diff(ToString.stringToArray(fromDoc.getContent()), ToString.stringToArray(toDoc.getContent())));
6004    }
6005   
 
6006  0 toggle public List<Delta> getContentDiff(String fromRev, String toRev, XWikiContext context)
6007    throws XWikiException, DifferentiationFailedException
6008    {
6009  0 XWikiDocument fromDoc = context.getWiki().getDocument(this, fromRev, context);
6010  0 XWikiDocument toDoc = context.getWiki().getDocument(this, toRev, context);
6011  0 return getContentDiff(fromDoc, toDoc, context);
6012    }
6013   
 
6014  0 toggle public List<Delta> getContentDiff(String fromRev, XWikiContext context)
6015    throws XWikiException, DifferentiationFailedException
6016    {
6017  0 XWikiDocument revdoc = context.getWiki().getDocument(this, fromRev, context);
6018  0 return getContentDiff(revdoc, this, context);
6019    }
6020   
 
6021  0 toggle public List<Delta> getLastChanges(XWikiContext context) throws XWikiException, DifferentiationFailedException
6022    {
6023  0 Version version = getRCSVersion();
6024  0 try {
6025  0 String prev = getDocumentArchive(context).getPrevVersion(version).toString();
6026  0 XWikiDocument prevDoc = context.getWiki().getDocument(this, prev, context);
6027   
6028  0 return getDeltas(
6029    Diff.diff(ToString.stringToArray(prevDoc.getContent()), ToString.stringToArray(getContent())));
6030    } catch (Exception ex) {
6031  0 LOGGER.debug("Exception getting differences from previous version: " + ex.getMessage());
6032    }
6033   
6034  0 return new ArrayList<Delta>();
6035    }
6036   
 
6037  0 toggle public List<Delta> getRenderedContentDiff(XWikiDocument fromDoc, XWikiDocument toDoc, XWikiContext context)
6038    throws XWikiException, DifferentiationFailedException
6039    {
6040  0 String originalContent = fromDoc.getRenderedContent(context);
6041  0 String newContent = toDoc.getRenderedContent(context);
6042   
6043  0 return getDeltas(Diff.diff(ToString.stringToArray(originalContent), ToString.stringToArray(newContent)));
6044    }
6045   
 
6046  0 toggle public List<Delta> getRenderedContentDiff(String fromRev, String toRev, XWikiContext context)
6047    throws XWikiException, DifferentiationFailedException
6048    {
6049  0 XWikiDocument fromDoc = context.getWiki().getDocument(this, fromRev, context);
6050  0 XWikiDocument toDoc = context.getWiki().getDocument(this, toRev, context);
6051   
6052  0 return getRenderedContentDiff(fromDoc, toDoc, context);
6053    }
6054   
 
6055  0 toggle public List<Delta> getRenderedContentDiff(String fromRev, XWikiContext context)
6056    throws XWikiException, DifferentiationFailedException
6057    {
6058  0 XWikiDocument revdoc = context.getWiki().getDocument(this, fromRev, context);
6059   
6060  0 return getRenderedContentDiff(revdoc, this, context);
6061    }
6062   
 
6063  1 toggle protected List<Delta> getDeltas(Revision rev)
6064    {
6065  1 List<Delta> list = new ArrayList<Delta>();
6066  2 for (int i = 0; i < rev.size(); i++) {
6067  1 list.add(rev.getDelta(i));
6068    }
6069   
6070  1 return list;
6071    }
6072   
 
6073  0 toggle public List<MetaDataDiff> getMetaDataDiff(String fromRev, String toRev, XWikiContext context) throws XWikiException
6074    {
6075  0 XWikiDocument fromDoc = context.getWiki().getDocument(this, fromRev, context);
6076  0 XWikiDocument toDoc = context.getWiki().getDocument(this, toRev, context);
6077   
6078  0 return getMetaDataDiff(fromDoc, toDoc, context);
6079    }
6080   
 
6081  0 toggle public List<MetaDataDiff> getMetaDataDiff(String fromRev, XWikiContext context) throws XWikiException
6082    {
6083  0 XWikiDocument revdoc = context.getWiki().getDocument(this, fromRev, context);
6084   
6085  0 return getMetaDataDiff(revdoc, this, context);
6086    }
6087   
 
6088  5 toggle public List<MetaDataDiff> getMetaDataDiff(XWikiDocument fromDoc, XWikiDocument toDoc, XWikiContext context)
6089    throws XWikiException
6090    {
6091  5 List<MetaDataDiff> list = new ArrayList<MetaDataDiff>();
6092   
6093  5 if (fromDoc == null || toDoc == null) {
6094  0 return list;
6095    }
6096   
6097  5 if (!fromDoc.getTitle().equals(toDoc.getTitle())) {
6098  1 list.add(new MetaDataDiff("title", fromDoc.getTitle(), toDoc.getTitle()));
6099    }
6100   
6101  5 if (ObjectUtils.notEqual(fromDoc.getRelativeParentReference(), toDoc.getRelativeParentReference())) {
6102  1 list.add(new MetaDataDiff("parent", fromDoc.getParent(), toDoc.getParent()));
6103    }
6104   
6105  5 if (ObjectUtils.notEqual(fromDoc.getAuthorReference(), toDoc.getAuthorReference())) {
6106  4 list.add(new MetaDataDiff("author", fromDoc.getAuthor(), toDoc.getAuthor()));
6107    }
6108   
6109  5 if (ObjectUtils.notEqual(fromDoc.getDocumentReference(), toDoc.getDocumentReference())) {
6110  0 list.add(new MetaDataDiff("reference", fromDoc.getDocumentReference(), toDoc.getDocumentReference()));
6111    }
6112   
6113  5 if (!fromDoc.getSpace().equals(toDoc.getSpace())) {
6114  0 list.add(new MetaDataDiff("web", fromDoc.getSpace(), toDoc.getSpace()));
6115    }
6116   
6117  5 if (!fromDoc.getName().equals(toDoc.getName())) {
6118  0 list.add(new MetaDataDiff("name", fromDoc.getName(), toDoc.getName()));
6119    }
6120   
6121  5 if (ObjectUtils.notEqual(fromDoc.getLocale(), toDoc.getLocale())) {
6122  0 list.add(new MetaDataDiff("language", fromDoc.getLanguage(), toDoc.getLanguage()));
6123    }
6124   
6125  5 if (ObjectUtils.notEqual(fromDoc.getDefaultLocale(), toDoc.getDefaultLocale())) {
6126  2 list.add(new MetaDataDiff("defaultLanguage", fromDoc.getDefaultLanguage(), toDoc.getDefaultLanguage()));
6127    }
6128   
6129  5 if (ObjectUtils.notEqual(fromDoc.getSyntax(), toDoc.getSyntax())) {
6130  0 list.add(new MetaDataDiff("syntax", fromDoc.getSyntax(), toDoc.getSyntax()));
6131    }
6132   
6133  5 if (fromDoc.isHidden() != toDoc.isHidden()) {
6134  0 list.add(new MetaDataDiff("hidden", fromDoc.isHidden(), toDoc.isHidden()));
6135    }
6136   
6137  5 return list;
6138    }
6139   
 
6140  0 toggle public List<List<ObjectDiff>> getObjectDiff(String fromRev, String toRev, XWikiContext context)
6141    throws XWikiException
6142    {
6143  0 XWikiDocument fromDoc = context.getWiki().getDocument(this, fromRev, context);
6144  0 XWikiDocument toDoc = context.getWiki().getDocument(this, toRev, context);
6145   
6146  0 return getObjectDiff(fromDoc, toDoc, context);
6147    }
6148   
 
6149  0 toggle public List<List<ObjectDiff>> getObjectDiff(String fromRev, XWikiContext context) throws XWikiException
6150    {
6151  0 XWikiDocument revdoc = context.getWiki().getDocument(this, fromRev, context);
6152   
6153  0 return getObjectDiff(revdoc, this, context);
6154    }
6155   
6156    /**
6157    * Return the object differences between two document versions. There is no hard requirement on the order of the two
6158    * versions, but the results are semantically correct only if the two versions are given in the right order.
6159    *
6160    * @param fromDoc The old ('before') version of the document.
6161    * @param toDoc The new ('after') version of the document.
6162    * @param context The {@link com.xpn.xwiki.XWikiContext context}.
6163    * @return The object differences. The returned list's elements are other lists, one for each changed object. The
6164    * inner lists contain {@link ObjectDiff} elements, one object for each changed property of the object.
6165    * Additionally, if the object was added or removed, then the first entry in the list will be an
6166    * "object-added" or "object-removed" marker.
6167    */
 
6168  517 toggle public List<List<ObjectDiff>> getObjectDiff(XWikiDocument fromDoc, XWikiDocument toDoc, XWikiContext context)
6169    {
6170  517 List<List<ObjectDiff>> difflist = new ArrayList<List<ObjectDiff>>();
6171   
6172    // Since objects could have been deleted or added, we iterate on both the old and the new
6173    // object collections.
6174    // First, iterate over the old objects.
6175  517 for (List<BaseObject> objects : fromDoc.getXObjects().values()) {
6176  567 for (BaseObject originalObj : objects) {
6177    // This happens when objects are deleted, and the document is still in the cache
6178    // storage.
6179  691 if (originalObj != null) {
6180  679 BaseObject newObj = toDoc.getXObject(originalObj.getXClassReference(), originalObj.getNumber());
6181  679 List<ObjectDiff> dlist;
6182  679 if (newObj == null) {
6183    // The object was deleted.
6184  14 dlist = new BaseObject().getDiff(originalObj, context);
6185  14 ObjectDiff deleteMarker =
6186    new ObjectDiff(originalObj.getXClassReference(), originalObj.getNumber(),
6187    originalObj.getGuid(), ObjectDiff.ACTION_OBJECTREMOVED, "", "", "", "");
6188  14 dlist.add(0, deleteMarker);
6189    } else {
6190    // The object exists in both versions, but might have been changed.
6191  665 dlist = newObj.getDiff(originalObj, context);
6192    }
6193  679 if (!dlist.isEmpty()) {
6194  158 difflist.add(dlist);
6195    }
6196    }
6197    }
6198    }
6199   
6200    // Second, iterate over the objects which are only in the new version.
6201  517 for (List<BaseObject> objects : toDoc.getXObjects().values()) {
6202  630 for (BaseObject newObj : objects) {
6203    // This happens when objects are deleted, and the document is still in the cache
6204    // storage.
6205  795 if (newObj != null) {
6206  764 BaseObject originalObj = fromDoc.getXObject(newObj.getXClassReference(), newObj.getNumber());
6207  764 if (originalObj == null) {
6208    // TODO: Refactor this so that getDiff() accepts null Object as input.
6209    // Only consider added objects, the other case was treated above.
6210  99 originalObj = new BaseObject();
6211  99 originalObj.setXClassReference(newObj.getRelativeXClassReference());
6212  99 originalObj.setNumber(newObj.getNumber());
6213  99 originalObj.setGuid(newObj.getGuid());
6214  99 List<ObjectDiff> dlist = newObj.getDiff(originalObj, context);
6215  99 ObjectDiff addMarker = new ObjectDiff(newObj.getXClassReference(), newObj.getNumber(),
6216    newObj.getGuid(), ObjectDiff.ACTION_OBJECTADDED, "", "", "", "");
6217  99 dlist.add(0, addMarker);
6218  99 if (!dlist.isEmpty()) {
6219  99 difflist.add(dlist);
6220    }
6221    }
6222    }
6223    }
6224    }
6225   
6226  517 return difflist;
6227    }
6228   
 
6229  462 toggle public List<List<ObjectDiff>> getClassDiff(XWikiDocument fromDoc, XWikiDocument toDoc, XWikiContext context)
6230    {
6231  462 List<List<ObjectDiff>> difflist = new ArrayList<List<ObjectDiff>>();
6232  462 BaseClass oldClass = fromDoc.getXClass();
6233  462 BaseClass newClass = toDoc.getXClass();
6234   
6235  462 if ((newClass == null) && (oldClass == null)) {
6236  0 return difflist;
6237    }
6238   
6239  462 List<ObjectDiff> dlist = newClass.getDiff(oldClass, context);
6240  462 if (!dlist.isEmpty()) {
6241  14 difflist.add(dlist);
6242    }
6243   
6244  462 return difflist;
6245    }
6246   
6247    /**
6248    * @param fromDoc
6249    * @param toDoc
6250    * @param context
6251    * @return
6252    */
 
6253  511 toggle public List<AttachmentDiff> getAttachmentDiff(XWikiDocument fromDoc, XWikiDocument toDoc, XWikiContext context)
6254    {
6255  511 List<AttachmentDiff> difflist = new ArrayList<AttachmentDiff>();
6256  511 for (XWikiAttachment origAttach : fromDoc.getAttachmentList()) {
6257  81 String fileName = origAttach.getFilename();
6258  81 XWikiAttachment newAttach = toDoc.getAttachment(fileName);
6259  81 if (newAttach == null) {
6260  9 difflist.add(new AttachmentDiff(fileName, org.xwiki.diff.Delta.Type.DELETE, origAttach, newAttach));
6261    } else {
6262  72 try {
6263  72 if (!origAttach.equalsData(newAttach, context)) {
6264  4 difflist
6265    .add(new AttachmentDiff(fileName, org.xwiki.diff.Delta.Type.CHANGE, origAttach, newAttach));
6266    }
6267    } catch (XWikiException e) {
6268  0 LOGGER.error("Failed to compare attachments [{}] and [{}]", origAttach.getReference(),
6269    newAttach.getReference(), e);
6270    }
6271    }
6272    }
6273   
6274  511 for (XWikiAttachment newAttach : toDoc.getAttachmentList()) {
6275  128 String fileName = newAttach.getFilename();
6276  128 XWikiAttachment origAttach = fromDoc.getAttachment(fileName);
6277  128 if (origAttach == null) {
6278  56 difflist.add(new AttachmentDiff(fileName, org.xwiki.diff.Delta.Type.INSERT, origAttach, newAttach));
6279    }
6280    }
6281   
6282  511 return difflist;
6283    }
6284   
6285    /**
6286    * Rename the current document and all the backlinks leading to it. Will also change parent field in all documents
6287    * which list the document we are renaming as their parent.
6288    * <p>
6289    * See {@link #rename(String, java.util.List, com.xpn.xwiki.XWikiContext)} for more details.
6290    *
6291    * @param newDocumentReference the new document reference
6292    * @param context the ubiquitous XWiki Context
6293    * @throws XWikiException in case of an error
6294    * @since 2.2M2
6295    */
 
6296  2 toggle public void rename(DocumentReference newDocumentReference, XWikiContext context) throws XWikiException
6297    {
6298  2 rename(newDocumentReference, getBackLinkedReferences(context), context);
6299    }
6300   
6301    /**
6302    * @deprecated since 2.2M2 use {@link #rename(DocumentReference, XWikiContext)}
6303    */
 
6304  1 toggle @Deprecated
6305    public void rename(String newDocumentName, XWikiContext context) throws XWikiException
6306    {
6307  1 rename(newDocumentName, getBackLinkedPages(context), context);
6308    }
6309   
6310    /**
6311    * Rename the current document and all the links pointing to it in the list of passed backlink documents. The
6312    * renaming algorithm takes into account the fact that there are several ways to write a link to a given page and
6313    * all those forms need to be renamed. For example the following links all point to the same page:
6314    * <ul>
6315    * <li>[Page]</li>
6316    * <li>[Page?param=1]</li>
6317    * <li>[currentwiki:Page]</li>
6318    * <li>[CurrentSpace.Page]</li>
6319    * <li>[currentwiki:CurrentSpace.Page]</li>
6320    * </ul>
6321    * <p>
6322    * Note: links without a space are renamed with the space added and all documents which have the document being
6323    * renamed as parent have their parent field set to "currentwiki:CurrentSpace.Page".
6324    * </p>
6325    *
6326    * @param newDocumentReference the new document reference
6327    * @param backlinkDocumentReferences the list of references of documents to parse and for which links will be
6328    * modified to point to the new document reference
6329    * @param context the ubiquitous XWiki Context
6330    * @throws XWikiException in case of an error
6331    * @since 2.2M2
6332    */
 
6333  2 toggle public void rename(DocumentReference newDocumentReference, List<DocumentReference> backlinkDocumentReferences,
6334    XWikiContext context) throws XWikiException
6335    {
6336  2 rename(newDocumentReference, backlinkDocumentReferences, getChildrenReferences(context), context);
6337    }
6338   
6339    /**
6340    * @deprecated since 2.2M2 use {@link #rename(DocumentReference, java.util.List, com.xpn.xwiki.XWikiContext)}
6341    */
 
6342  1 toggle @Deprecated
6343    public void rename(String newDocumentName, List<String> backlinkDocumentNames, XWikiContext context)
6344    throws XWikiException
6345    {
6346  1 rename(newDocumentName, backlinkDocumentNames, getChildren(context), context);
6347    }
6348   
6349    /**
6350    * Same as {@link #rename(DocumentReference, List, XWikiContext)} but the list of documents having the current
6351    * document as their parent is passed in parameter.
6352    *
6353    * @param newDocumentReference the new document reference
6354    * @param backlinkDocumentReferences the list of references of documents to parse and for which links will be
6355    * modified to point to the new document reference
6356    * @param childDocumentReferences the list of references of document whose parent field will be set to the new
6357    * document reference
6358    * @param context the ubiquitous XWiki Context
6359    * @throws XWikiException in case of an error
6360    * @since 2.2M2
6361    */
 
6362  5 toggle public void rename(DocumentReference newDocumentReference, List<DocumentReference> backlinkDocumentReferences,
6363    List<DocumentReference> childDocumentReferences, XWikiContext context) throws XWikiException
6364    {
6365    // TODO: Do all this in a single DB transaction as otherwise the state will be unknown if
6366    // something fails in the middle...
6367   
6368    // TODO: Why do we verify if the document has just been created and not been saved.
6369    // If the user is trying to rename to the same name... In that case, simply exits for efficiency.
6370  5 if (isNew() || getDocumentReference().equals(newDocumentReference)) {
6371  0 return;
6372    }
6373   
6374    // Grab the xwiki object, it gets used a few times.
6375  5 XWiki xwiki = context.getWiki();
6376   
6377    // Step 1: Copy the document and all its translations under a new document with the new reference.
6378  5 xwiki.copyDocument(getDocumentReference(), newDocumentReference, false, context);
6379   
6380    // Step 2: For each child document, update its parent reference.
6381  5 if (childDocumentReferences != null) {
6382  5 for (DocumentReference childDocumentReference : childDocumentReferences) {
6383  2 XWikiDocument childDocument = xwiki.getDocument(childDocumentReference, context);
6384  2 String compactReference = getCompactEntityReferenceSerializer().serialize(newDocumentReference);
6385  2 childDocument.setParent(compactReference);
6386  2 String saveMessage = localizePlainOrKey("core.comment.renameParent", compactReference);
6387  2 childDocument.setAuthorReference(context.getUserReference());
6388  2 xwiki.saveDocument(childDocument, saveMessage, true, context);
6389    }
6390    }
6391   
6392    // Step 3: For each backlink to rename, parse the backlink document and replace the links with the new name.
6393  5 for (DocumentReference backlinkDocumentReference : backlinkDocumentReferences) {
6394  3 XWikiDocument backlinkRootDocument = xwiki.getDocument(backlinkDocumentReference, context);
6395   
6396    // Update default locale instance
6397  3 renameLinks(backlinkRootDocument, getDocumentReference(), newDocumentReference, context);
6398   
6399    // Update translations
6400  3 for (Locale locale : backlinkRootDocument.getTranslationLocales(context)) {
6401  0 XWikiDocument backlinkDocument = backlinkRootDocument.getTranslatedDocument(locale, context);
6402   
6403  0 renameLinks(backlinkDocument, getDocumentReference(), newDocumentReference, context);
6404    }
6405    }
6406   
6407    // Get new document
6408  5 XWikiDocument newDocument = xwiki.getDocument(newDocumentReference, context);
6409   
6410    // Step 4: Refactor the relative links contained in the document to make sure they are relative to the new
6411    // document's location.
6412  5 if (Utils.getContextComponentManager().hasComponent(BlockRenderer.class, getSyntax().toIdString())) {
6413    // Only support syntax for which a renderer is provided
6414   
6415  4 LinkedResourceHelper linkedResourceHelper = Utils.getComponent(LinkedResourceHelper.class);
6416   
6417  4 DocumentReference oldDocumentReference = getDocumentReference();
6418   
6419  4 XDOM newDocumentXDOM = newDocument.getXDOM();
6420  4 List<Block> blocks = linkedResourceHelper.getBlocks(newDocumentXDOM);
6421   
6422    // FIXME: Duplicate code. See org.xwiki.refactoring.internal.DefaultLinkRefactoring#updateRelativeLinks in
6423    // xwiki-platform-refactoring-default
6424  4 boolean modified = false;
6425  4 for (Block block : blocks) {
6426  1 ResourceReference resourceReference = linkedResourceHelper.getResourceReference(block);
6427  1 if (resourceReference == null) {
6428    // Skip invalid blocks.
6429  0 continue;
6430    }
6431   
6432  1 ResourceType resourceType = resourceReference.getType();
6433   
6434    // TODO: support ATTACHMENT as well.
6435  1 if (!ResourceType.DOCUMENT.equals(resourceType) && !ResourceType.SPACE.equals(resourceType)) {
6436    // We are currently only interested in Document or Space references.
6437  0 continue;
6438    }
6439   
6440    // current link, use the old document's reference to fill in blanks.
6441  1 EntityReference oldLinkReference = getResourceReferenceEntityReferenceResolver()
6442    .resolve(resourceReference, null, oldDocumentReference);
6443    // new link, use the new document's reference to fill in blanks.
6444  1 EntityReference newLinkReference = getResourceReferenceEntityReferenceResolver()
6445    .resolve(resourceReference, null, newDocumentReference);
6446   
6447    // If the new and old link references don`t match, then we must update the relative link.
6448  1 if (!newLinkReference.equals(oldLinkReference)) {
6449  1 modified = true;
6450   
6451    // Serialize the old (original) link relative to the new document's location, in compact form.
6452  1 String serializedLinkReference =
6453    getCompactWikiEntityReferenceSerializer().serialize(oldLinkReference, newDocumentReference);
6454   
6455    // Update the reference in the XDOM.
6456  1 linkedResourceHelper.setResourceReferenceString(block, serializedLinkReference);
6457    }
6458    }
6459   
6460    // Set the new content and save document if needed
6461  4 if (modified) {
6462  1 newDocument.setContent(newDocumentXDOM);
6463  1 newDocument.setAuthorReference(context.getUserReference());
6464  1 xwiki.saveDocument(newDocument, context);
6465    }
6466    }
6467   
6468    // Step 5: Delete the old document
6469  5 xwiki.deleteDocument(this, context);
6470   
6471    // Step 6: The current document needs to point to the renamed document as otherwise it's pointing to an
6472    // invalid XWikiDocument object as it's been deleted...
6473  5 clone(newDocument);
6474    }
6475   
6476    /**
6477    * Rename links in passed document and save it if needed.
6478    */
 
6479  3 toggle private void renameLinks(XWikiDocument backlinkDocument, DocumentReference oldLink, DocumentReference newLink,
6480    XWikiContext context) throws XWikiException
6481    {
6482    // FIXME: Duplicate code. See org.xwiki.refactoring.internal.DefaultLinkRefactoring#renameLinks in
6483    // xwiki-platform-refactoring-default
6484  3 getOldRendering().renameLinks(backlinkDocument, oldLink, newLink, context);
6485   
6486    // Save if content changed
6487  3 if (backlinkDocument.isContentDirty()) {
6488  3 String saveMessage =
6489    localizePlainOrKey("core.comment.renameLink", getCompactEntityReferenceSerializer().serialize(newLink));
6490  3 backlinkDocument.setAuthorReference(context.getUserReference());
6491  3 context.getWiki().saveDocument(backlinkDocument, saveMessage, true, context);
6492    }
6493    }
6494   
6495    /**
6496    * @deprecated since 2.2M2 use {@link #rename(DocumentReference, List, List, com.xpn.xwiki.XWikiContext)}
6497    */
 
6498  1 toggle @Deprecated
6499    public void rename(String newDocumentName, List<String> backlinkDocumentNames, List<String> childDocumentNames,
6500    XWikiContext context) throws XWikiException
6501    {
6502  1 List<DocumentReference> backlinkDocumentReferences = new ArrayList<DocumentReference>();
6503  1 for (String backlinkDocumentName : backlinkDocumentNames) {
6504  0 backlinkDocumentReferences.add(getCurrentMixedDocumentReferenceResolver().resolve(backlinkDocumentName));
6505    }
6506   
6507  1 List<DocumentReference> childDocumentReferences = new ArrayList<DocumentReference>();
6508  1 for (String childDocumentName : childDocumentNames) {
6509  0 childDocumentReferences.add(getCurrentMixedDocumentReferenceResolver().resolve(childDocumentName));
6510    }
6511   
6512  1 rename(getCurrentMixedDocumentReferenceResolver().resolve(newDocumentName), backlinkDocumentReferences,
6513    childDocumentReferences, context);
6514    }
6515   
6516    /**
6517    * @since 2.2M1
6518    */
 
6519  126 toggle public XWikiDocument copyDocument(DocumentReference newDocumentReference, XWikiContext context)
6520    throws XWikiException
6521    {
6522  126 loadAttachments(context);
6523  126 loadArchive(context);
6524   
6525  126 XWikiDocument newdoc = duplicate(newDocumentReference);
6526   
6527    // If the copied document has a title set to the original page name then set the new title to be the new page
6528    // name.
6529  126 if (StringUtils.equals(newdoc.getTitle(), getPrettyName(this.getDocumentReference()))) {
6530  9 newdoc.setTitle(getPrettyName(newDocumentReference));
6531    }
6532   
6533  126 newdoc.setOriginalDocument(null);
6534  126 newdoc.setContentDirty(true);
6535  126 newdoc.getXClass().setOwnerDocument(newdoc);
6536   
6537  126 XWikiDocumentArchive archive = getDocumentArchive();
6538  126 if (archive != null) {
6539  113 newdoc.setDocumentArchive(archive.clone(newdoc.getId(), context));
6540    }
6541   
6542  126 return newdoc;
6543    }
6544   
6545    /**
6546    * Avoid the technical "WebHome" name.
6547    *
6548    * @param documentReference a document reference
6549    * @return the last space name if the document is the home of a space, the document name otherwise
6550    */
 
6551  135 toggle private String getPrettyName(DocumentReference documentReference)
6552    {
6553  135 EntityReferenceProvider defaultEntityReferenceProvider = Utils.getComponent(EntityReferenceProvider.class);
6554  135 if (defaultEntityReferenceProvider.getDefaultReference(documentReference.getType()).getName()
6555    .equals(documentReference.getName())) {
6556  10 return documentReference.getLastSpaceReference().getName();
6557    }
6558  125 return documentReference.getName();
6559    }
6560   
6561    /**
6562    * @deprecated since 2.2M1 use {@link #copyDocument(DocumentReference, XWikiContext)} instead
6563    */
 
6564  5 toggle @Deprecated
6565    public XWikiDocument copyDocument(String newDocumentName, XWikiContext context) throws XWikiException
6566    {
6567  5 return copyDocument(getCurrentMixedDocumentReferenceResolver().resolve(newDocumentName), context);
6568    }
6569   
 
6570  1530 toggle public XWikiLock getLock(XWikiContext context) throws XWikiException
6571    {
6572  1530 XWikiLock theLock = getStore(context).loadLock(getId(), context, true);
6573  1527 if (theLock != null) {
6574  863 int timeout = context.getWiki().getXWikiPreferenceAsInt("lock_Timeout", 30 * 60, context);
6575  864 if (theLock.getDate().getTime() + timeout * 1000 < new Date().getTime()) {
6576  0 getStore(context).deleteLock(theLock, context, true);
6577  0 theLock = null;
6578    }
6579    }
6580   
6581  1527 return theLock;
6582    }
6583   
 
6584  552 toggle public void setLock(String userName, XWikiContext context) throws XWikiException
6585    {
6586  555 XWikiLock lock = new XWikiLock(getId(), userName);
6587  558 getStore(context).saveLock(lock, context, true);
6588    }
6589   
 
6590  264 toggle public void removeLock(XWikiContext context) throws XWikiException
6591    {
6592  264 XWikiLock lock = getStore(context).loadLock(getId(), context, true);
6593  264 if (lock != null) {
6594  263 getStore(context).deleteLock(lock, context, true);
6595    }
6596    }
6597   
 
6598  0 toggle public void insertText(String text, String marker, XWikiContext context) throws XWikiException
6599    {
6600  0 setContent(StringUtils.replaceOnce(getContent(), marker, text + marker));
6601  0 context.getWiki().saveDocument(this, context);
6602    }
6603   
 
6604  0 toggle public Object getWikiNode()
6605    {
6606  0 return this.wikiNode;
6607    }
6608   
 
6609  13983 toggle public void setWikiNode(Object wikiNode)
6610    {
6611  13983 this.wikiNode = wikiNode;
6612    }
6613   
6614    /**
6615    * @since 2.2M1
6616    */
 
6617  42793 toggle public String getXClassXML()
6618    {
6619  42788 return this.xClassXML;
6620    }
6621   
6622    /**
6623    * @deprecated since 2.2M1 use {@link #getXClassXML()} instead Hibernate uses this through reflection. It cannot be
6624    * removed without altering hibernate.cfg.xml
6625    */
 
6626  7972 toggle @Deprecated
6627    public String getxWikiClassXML()
6628    {
6629  7972 return getXClassXML();
6630    }
6631   
6632    /**
6633    * @since 2.2M1
6634    */
 
6635  38638 toggle public void setXClassXML(String xClassXML)
6636    {
6637  38642 this.xClassXML = xClassXML;
6638    }
6639   
6640    /**
6641    * @deprecated since 2.2M1 use {@link #setXClassXML(String)} ()} instead Hibernate uses this through reflection. It
6642    * cannot be removed without altering hibernate.cfg.xml
6643    */
 
6644  3643 toggle @Deprecated
6645    public void setxWikiClassXML(String xClassXML)
6646    {
6647  3644 setXClassXML(xClassXML);
6648    }
6649   
 
6650  39304 toggle public int getElements()
6651    {
6652  39303 return this.elements;
6653    }
6654   
 
6655  35135 toggle public void setElements(int elements)
6656    {
6657  35135 this.elements = elements;
6658    }
6659   
 
6660  7610 toggle public void setElement(int element, boolean toggle)
6661    {
6662  7610 if (toggle) {
6663  2577 this.elements = this.elements | element;
6664    } else {
6665  5033 this.elements = this.elements & (~element);
6666    }
6667    }
6668   
 
6669  14831 toggle public boolean hasElement(int element)
6670    {
6671  14834 return ((this.elements & element) == element);
6672    }
6673   
6674    /**
6675    * Gets the default edit mode for this document. An edit mode (other than the default "edit") can be enforced by
6676    * creating an {@code XWiki.EditModeClass} object in the current document, with the appropriate value for the
6677    * defaultEditMode property, or by adding this object in a sheet included by the document. This function also falls
6678    * back on the old {@code SheetClass}, deprecated since 3.1M2, which can be attached to included documents to
6679    * specify that the current document should be edited inline.
6680    *
6681    * @return the default edit mode for this document ("edit" or "inline" usually)
6682    * @param context the context of the request for this document
6683    * @throws XWikiException since XWiki 6.3M1 it's not used anymore and "edit" is returned in case of error, with an
6684    * error log
6685    */
 
6686  26740 toggle public String getDefaultEditMode(XWikiContext context) throws XWikiException
6687    {
6688  26741 try {
6689  26733 return getDefaultEditModeInternal(context);
6690    } catch (Exception e) {
6691    // If an error happens then we default to the "edit" mode. We don't want to fail by throwing an exception
6692    // since it'll lead to several errors in the UI (such as when evaluating contentview.vm for example).
6693  0 LOGGER.error("Failed to get the default edit mode for [{}]", getDocumentReference(), e);
6694  0 return "edit";
6695    }
6696    }
6697   
 
6698  26734 toggle private String getDefaultEditModeInternal(XWikiContext context) throws XWikiException
6699    {
6700  26736 String editModeProperty = "defaultEditMode";
6701  26738 DocumentReference editModeClass =
6702    getCurrentReferenceDocumentReferenceResolver().resolve(XWikiConstant.EDIT_MODE_CLASS);
6703    // check if the current document has any edit mode class object attached to it, and read the edit mode from it
6704  26742 BaseObject editModeObject = this.getXObject(editModeClass);
6705  26735 if (editModeObject != null) {
6706  0 String defaultEditMode = editModeObject.getStringValue(editModeProperty);
6707  0 if (StringUtils.isEmpty(defaultEditMode)) {
6708  0 return "edit";
6709    } else {
6710  0 return defaultEditMode;
6711    }
6712    }
6713    // otherwise look for included documents
6714  26737 com.xpn.xwiki.XWiki xwiki = context.getWiki();
6715  26737 if (is10Syntax()) {
6716  10 if (getContent().indexOf("includeForm(") != -1) {
6717  2 return "inline";
6718    }
6719    } else {
6720    // Algorithm: look in all include macros and for all document included check if one of them
6721    // has an EditModeClass object attached to it, or a SheetClass object (deprecated since 3.1M2) attached to
6722    // it. If so then the edit mode is inline.
6723   
6724    // Find all include macros and extract the document names
6725    // TODO: Is there a good way not to hardcode the macro name? The macro itself shouldn't know
6726    // its own name since it's a deployment time concern.
6727  26726 for (Block macroBlock : getXDOM().getBlocks(new MacroBlockMatcher("include"), Axes.CHILD)) {
6728    // Find the document reference to include by checking the macro's "reference" parameter.
6729    // For backward-compatibility we also check for a "document" parameter since this is the parameter name
6730    // that was used prior to XWiki 3.4M1 when the "reference" one was introduced and thus when the
6731    // "document" one was deprecated.
6732  1160 String includedDocumentReference = macroBlock.getParameter("reference");
6733  1160 if (includedDocumentReference == null) {
6734  0 includedDocumentReference = macroBlock.getParameter("document");
6735    }
6736  1160 if (includedDocumentReference != null) {
6737    // Resolve the document name into a valid Reference
6738  1160 DocumentReference documentReference =
6739    getCurrentMixedDocumentReferenceResolver().resolve(includedDocumentReference);
6740  1160 XWikiDocument includedDocument = xwiki.getDocument(documentReference, context);
6741  1160 if (!includedDocument.isNew()) {
6742    // get the edit mode object, first the new class and then the deprecated class if new class
6743    // is not found
6744  1108 editModeObject = includedDocument.getXObject(editModeClass);
6745  1108 if (editModeObject == null) {
6746  1108 editModeObject = includedDocument.getXObject(SHEETCLASS_REFERENCE);
6747    }
6748  1108 if (editModeObject != null) {
6749    // Use the user-defined default edit mode if set.
6750  2 String defaultEditMode = editModeObject.getStringValue(editModeProperty);
6751  2 if (StringUtils.isBlank(defaultEditMode)) {
6752    // TODO: maybe here the real value should be returned if the object is edit mode class,
6753    // and inline only if the object is sheetclass
6754  2 return "inline";
6755    } else {
6756  0 return defaultEditMode;
6757    }
6758    }
6759    }
6760    }
6761    }
6762    }
6763   
6764  26737 return "edit";
6765    }
6766   
 
6767  0 toggle public String getDefaultEditURL(XWikiContext context) throws XWikiException
6768    {
6769  0 String editMode = getDefaultEditMode(context);
6770   
6771  0 if ("inline".equals(editMode)) {
6772  0 return getEditURL("inline", "", context);
6773    } else {
6774  0 com.xpn.xwiki.XWiki xwiki = context.getWiki();
6775  0 String editor = xwiki.getEditorPreference(context);
6776  0 return getEditURL("edit", editor, context);
6777    }
6778    }
6779   
 
6780  0 toggle public String getEditURL(String action, String mode, XWikiContext context) throws XWikiException
6781    {
6782  0 com.xpn.xwiki.XWiki xwiki = context.getWiki();
6783  0 String language = "";
6784  0 XWikiDocument tdoc = (XWikiDocument) context.get("tdoc");
6785  0 String realLang = tdoc.getRealLanguage(context);
6786  0 if ((xwiki.isMultiLingual(context) == true) && (!realLang.equals(""))) {
6787  0 language = realLang;
6788    }
6789   
6790  0 return getEditURL(action, mode, language, context);
6791    }
6792   
 
6793  0 toggle public String getEditURL(String action, String mode, String language, XWikiContext context)
6794    {
6795  0 StringBuilder editparams = new StringBuilder();
6796  0 if (!mode.equals("")) {
6797  0 editparams.append("xpage=");
6798  0 editparams.append(mode);
6799    }
6800   
6801  0 if (!language.equals("")) {
6802  0 if (!mode.equals("")) {
6803  0 editparams.append("&");
6804    }
6805  0 editparams.append("language=");
6806  0 editparams.append(language);
6807    }
6808   
6809  0 return getURL(action, editparams.toString(), context);
6810    }
6811   
 
6812  46625 toggle public String getDefaultTemplate()
6813    {
6814  46622 if (this.defaultTemplate == null) {
6815  16517 return "";
6816    } else {
6817  30102 return this.defaultTemplate;
6818    }
6819    }
6820   
 
6821  42638 toggle public void setDefaultTemplate(String defaultTemplate)
6822    {
6823  42638 this.defaultTemplate = defaultTemplate;
6824   
6825  42635 setMetaDataDirty(true);
6826    }
6827   
 
6828  0 toggle public Vector<BaseObject> getComments()
6829    {
6830  0 return getComments(true);
6831    }
6832   
6833    /**
6834    * @return the syntax of the document
6835    * @since 2.3M1
6836    */
 
6837  141212 toggle @Override
6838    public Syntax getSyntax()
6839    {
6840    // Can't be initialized in the XWikiDocument constructor because #getDefaultDocumentSyntax() need to create a
6841    // XWikiDocument object to get preferences from wiki preferences pages and would thus generate an infinite loop
6842  141224 if (isNew() && this.syntax == null) {
6843  17044 this.syntax = getDefaultDocumentSyntax();
6844    }
6845   
6846  141219 return this.syntax;
6847    }
6848   
6849    /**
6850    * {@inheritDoc}
6851    * <p>
6852    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
6853    * </p>
6854    *
6855    * @see org.xwiki.bridge.DocumentModelBridge#getSyntaxId()
6856    * @deprecated since 2.3M1, use {link #getSyntax()} instead
6857    */
 
6858  48031 toggle @Override
6859    @Deprecated
6860    public String getSyntaxId()
6861    {
6862  48029 return getSyntax().toIdString();
6863    }
6864   
6865    /**
6866    * @param syntax the new syntax to set for this document
6867    * @see #getSyntax()
6868    * @since 2.3M1
6869    */
 
6870  44831 toggle public void setSyntax(Syntax syntax)
6871    {
6872  44836 if (ObjectUtils.notEqual(this.syntax, syntax)) {
6873  39610 this.syntax = syntax;
6874    // invalidate parsed xdom
6875  39608 this.xdomCache = null;
6876    }
6877    }
6878   
6879    /**
6880    * Note that this method cannot be removed for now since it's used by Hibernate for saving a XWikiDocument.
6881    *
6882    * @param syntaxId the new syntax id to set (e.g. {@code xwiki/2.0}, {@code xwiki/2.1}, etc)
6883    * @see #getSyntaxId()
6884    * @deprecated since 2.3M1, use {link #setSyntax(Syntax)} instead
6885    */
 
6886  3648 toggle @Deprecated
6887    public void setSyntaxId(String syntaxId)
6888    {
6889  3648 Syntax syntax;
6890   
6891    // In order to preserve backward-compatibility with previous versions of XWiki in which the notion of Syntax Id
6892    // did not exist, we check the passed syntaxId parameter. Since this parameter comes from the database (it's
6893    // called automatically by Hibernate) it can be NULL or empty. In this case we consider the document is in
6894    // syntax/1.0 syntax.
6895  3644 if (StringUtils.isBlank(syntaxId)) {
6896  0 syntax = Syntax.XWIKI_1_0;
6897    } else {
6898  3644 try {
6899  3645 syntax = getSyntaxFactory().createSyntaxFromIdString(syntaxId);
6900    } catch (ParseException e) {
6901  0 syntax = getDefaultDocumentSyntax();
6902  0 LOGGER.warn("Failed to set syntax [" + syntaxId + "] for ["
6903    + getDefaultEntityReferenceSerializer().serialize(getDocumentReference()) + "], setting syntax ["
6904    + syntax.toIdString() + "] instead.", e);
6905    }
6906    }
6907   
6908  3647 setSyntax(syntax);
6909    }
6910   
 
6911  850 toggle public Vector<BaseObject> getComments(boolean asc)
6912    {
6913  850 List<BaseObject> list = getXObjects(COMMENTSCLASS_REFERENCE);
6914  850 if (list == null) {
6915  835 return null;
6916  15 } else if (asc) {
6917  15 return new Vector<BaseObject>(list);
6918    } else {
6919  0 Vector<BaseObject> newlist = new Vector<BaseObject>();
6920  0 for (int i = list.size() - 1; i >= 0; i--) {
6921  0 newlist.add(list.get(i));
6922    }
6923  0 return newlist;
6924    }
6925    }
6926   
 
6927  0 toggle public boolean isCurrentUserCreator(XWikiContext context)
6928    {
6929  0 return isCreator(context.getUserReference());
6930    }
6931   
6932    /**
6933    * @deprecated use {@link #isCreator(DocumentReference)} instead
6934    */
 
6935  0 toggle @Deprecated
6936    public boolean isCreator(String username)
6937    {
6938  0 if (username.equals(XWikiRightService.GUEST_USER_FULLNAME)) {
6939  0 return false;
6940    }
6941   
6942  0 return username.equals(getCreator());
6943    }
6944   
 
6945  0 toggle public boolean isCreator(DocumentReference username)
6946    {
6947  0 if (username == null) {
6948  0 return false;
6949    }
6950   
6951  0 return username.equals(getCreatorReference());
6952    }
6953   
 
6954  11 toggle public boolean isCurrentUserPage(XWikiContext context)
6955    {
6956  11 DocumentReference userReference = context.getUserReference();
6957  11 if (userReference == null) {
6958  0 return false;
6959    }
6960   
6961  11 return userReference.equals(getDocumentReference());
6962    }
6963   
 
6964  0 toggle public boolean isCurrentLocalUserPage(XWikiContext context)
6965    {
6966  0 final DocumentReference userRef = context.getUserReference();
6967  0 return userRef != null && userRef.equals(this.getDocumentReference());
6968    }
6969   
 
6970  1978 toggle public void resetArchive(XWikiContext context) throws XWikiException
6971    {
6972  1978 boolean hasVersioning = context.getWiki().hasVersioning(context);
6973  1978 if (hasVersioning) {
6974  1978 getVersioningStore(context).resetRCSArchive(this, true, context);
6975    }
6976    }
6977   
6978    /**
6979    * Adds an object from an new object creation form.
6980    *
6981    * @since 2.2M2
6982    */
 
6983  0 toggle public BaseObject addXObjectFromRequest(XWikiContext context) throws XWikiException
6984    {
6985    // Read info in object
6986  0 ObjectAddForm form = new ObjectAddForm();
6987  0 form.setRequest(context.getRequest());
6988  0 form.readRequest();
6989   
6990  0 EntityReference classReference = getXClassEntityReferenceResolver().resolve(form.getClassName(),
6991    EntityType.DOCUMENT, getDocumentReference());
6992  0 BaseObject object = newXObject(classReference, context);
6993  0 BaseClass baseclass = object.getXClass(context);
6994  0 baseclass.fromMap(form.getObject(LOCAL_REFERENCE_SERIALIZER.serialize(resolveClassReference(classReference))),
6995    object);
6996   
6997  0 return object;
6998    }
6999   
7000    /**
7001    * Adds an object from an new object creation form.
7002    *
7003    * @since 2.2.3
7004    */
 
7005  0 toggle public BaseObject addXObjectFromRequest(EntityReference classReference, XWikiContext context) throws XWikiException
7006    {
7007  0 return addXObjectFromRequest(classReference, "", 0, context);
7008    }
7009   
7010    /**
7011    * @deprecated since 2.2M2 use {@link #addXObjectFromRequest(EntityReference, XWikiContext)}
7012    */
 
7013  0 toggle @Deprecated
7014    public BaseObject addObjectFromRequest(String className, XWikiContext context) throws XWikiException
7015    {
7016  0 return addObjectFromRequest(className, "", 0, context);
7017    }
7018   
7019    /**
7020    * Adds an object from an new object creation form.
7021    *
7022    * @since 2.2M2
7023    */
 
7024  0 toggle public BaseObject addXObjectFromRequest(DocumentReference classReference, String prefix, XWikiContext context)
7025    throws XWikiException
7026    {
7027  0 return addXObjectFromRequest(classReference, prefix, 0, context);
7028    }
7029   
7030    /**
7031    * @deprecated since 2.2M2 use {@link #addXObjectFromRequest(DocumentReference, String, XWikiContext)}
7032    */
 
7033  0 toggle @Deprecated
7034    public BaseObject addObjectFromRequest(String className, String prefix, XWikiContext context) throws XWikiException
7035    {
7036  0 return addObjectFromRequest(className, prefix, 0, context);
7037    }
7038   
7039    /**
7040    * Adds multiple objects from an new objects creation form.
7041    *
7042    * @since 2.2M2
7043    */
 
7044  0 toggle public List<BaseObject> addXObjectsFromRequest(DocumentReference classReference, XWikiContext context)
7045    throws XWikiException
7046    {
7047  0 return addXObjectsFromRequest(classReference, "", context);
7048    }
7049   
7050    /**
7051    * @deprecated since 2.2M2 use {@link #addXObjectsFromRequest(DocumentReference, XWikiContext)}
7052    */
 
7053  0 toggle @Deprecated
7054    public List<BaseObject> addObjectsFromRequest(String className, XWikiContext context) throws XWikiException
7055    {
7056  0 return addObjectsFromRequest(className, "", context);
7057    }
7058   
7059    /**
7060    * Adds multiple objects from an new objects creation form.
7061    *
7062    * @since 2.2M2
7063    */
 
7064  0 toggle public List<BaseObject> addXObjectsFromRequest(DocumentReference classReference, String pref, XWikiContext context)
7065    throws XWikiException
7066    {
7067  0 @SuppressWarnings("unchecked")
7068    Map<String, String[]> map = context.getRequest().getParameterMap();
7069  0 List<Integer> objectsNumberDone = new ArrayList<Integer>();
7070  0 List<BaseObject> objects = new ArrayList<BaseObject>();
7071  0 String start = pref + LOCAL_REFERENCE_SERIALIZER.serialize(classReference) + "_";
7072   
7073  0 for (String name : map.keySet()) {
7074  0 if (name.startsWith(start)) {
7075  0 int pos = name.indexOf('_', start.length() + 1);
7076  0 String prefix = name.substring(0, pos);
7077  0 int num = Integer.decode(prefix.substring(prefix.lastIndexOf('_') + 1)).intValue();
7078  0 if (!objectsNumberDone.contains(Integer.valueOf(num))) {
7079  0 objectsNumberDone.add(Integer.valueOf(num));
7080  0 objects.add(addXObjectFromRequest(classReference, pref, num, context));
7081    }
7082    }
7083    }
7084   
7085  0 return objects;
7086    }
7087   
7088    /**
7089    * @deprecated since 2.2M2 use {@link #addXObjectsFromRequest(DocumentReference, String, XWikiContext)}
7090    */
 
7091  0 toggle @Deprecated
7092    public List<BaseObject> addObjectsFromRequest(String className, String pref, XWikiContext context)
7093    throws XWikiException
7094    {
7095  0 return addXObjectsFromRequest(resolveClassReference(className), pref, context);
7096    }
7097   
7098    /**
7099    * Adds object from an new object creation form.
7100    *
7101    * @since 2.2M2
7102    */
 
7103  0 toggle public BaseObject addXObjectFromRequest(DocumentReference classReference, int num, XWikiContext context)
7104    throws XWikiException
7105    {
7106  0 return addXObjectFromRequest(classReference, "", num, context);
7107    }
7108   
7109    /**
7110    * @deprecated since 2.2M2 use {@link #addXObjectFromRequest(DocumentReference, int, XWikiContext)}
7111    */
 
7112  0 toggle @Deprecated
7113    public BaseObject addObjectFromRequest(String className, int num, XWikiContext context) throws XWikiException
7114    {
7115  0 return addObjectFromRequest(className, "", num, context);
7116    }
7117   
7118    /**
7119    * Adds object from an new object creation form.
7120    *
7121    * @since 2.2.3
7122    */
 
7123  0 toggle public BaseObject addXObjectFromRequest(EntityReference classReference, String prefix, int num,
7124    XWikiContext context) throws XWikiException
7125    {
7126  0 BaseObject object = newXObject(classReference, context);
7127  0 BaseClass baseclass = object.getXClass(context);
7128  0 String newPrefix =
7129    prefix + LOCAL_REFERENCE_SERIALIZER.serialize(resolveClassReference(classReference)) + "_" + num;
7130  0 baseclass.fromMap(Util.getObject(context.getRequest(), newPrefix), object);
7131   
7132  0 return object;
7133    }
7134   
7135    /**
7136    * @deprecated since 2.2M2 use {@link #addXObjectFromRequest(EntityReference, String, int, XWikiContext)}
7137    */
 
7138  0 toggle @Deprecated
7139    public BaseObject addObjectFromRequest(String className, String prefix, int num, XWikiContext context)
7140    throws XWikiException
7141    {
7142  0 return addXObjectFromRequest(resolveClassReference(className), prefix, num, context);
7143    }
7144   
7145    /**
7146    * Adds an object from an new object creation form.
7147    *
7148    * @since 2.2.3
7149    */
 
7150  0 toggle public BaseObject updateXObjectFromRequest(EntityReference classReference, XWikiContext context)
7151    throws XWikiException
7152    {
7153  0 return updateXObjectFromRequest(classReference, "", context);
7154    }
7155   
7156    /**
7157    * @deprecated since 2.2M2 use {@link #updateXObjectFromRequest(EntityReference, XWikiContext)}
7158    */
 
7159  0 toggle @Deprecated
7160    public BaseObject updateObjectFromRequest(String className, XWikiContext context) throws XWikiException
7161    {
7162  0 return updateObjectFromRequest(className, "", context);
7163    }
7164   
7165    /**
7166    * Adds an object from an new object creation form.
7167    *
7168    * @since 2.2.3
7169    */
 
7170  0 toggle public BaseObject updateXObjectFromRequest(EntityReference classReference, String prefix, XWikiContext context)
7171    throws XWikiException
7172    {
7173  0 return updateXObjectFromRequest(classReference, prefix, 0, context);
7174    }
7175   
7176    /**
7177    * @deprecated since 2.2M2 use {@link #updateXObjectFromRequest(EntityReference, String, XWikiContext)}
7178    */
 
7179  0 toggle @Deprecated
7180    public BaseObject updateObjectFromRequest(String className, String prefix, XWikiContext context)
7181    throws XWikiException
7182    {
7183  0 return updateObjectFromRequest(className, prefix, 0, context);
7184    }
7185   
7186    /**
7187    * Adds an object from an new object creation form.
7188    *
7189    * @since 2.2.3
7190    */
 
7191  0 toggle public BaseObject updateXObjectFromRequest(EntityReference classReference, String prefix, int num,
7192    XWikiContext context) throws XWikiException
7193    {
7194  0 DocumentReference absoluteClassReference = resolveClassReference(classReference);
7195  0 int nb;
7196  0 BaseObject oldobject = getXObject(absoluteClassReference, num);
7197  0 if (oldobject == null) {
7198  0 nb = createXObject(classReference, context);
7199  0 oldobject = getXObject(absoluteClassReference, nb);
7200    } else {
7201  0 nb = oldobject.getNumber();
7202    }
7203  0 BaseClass baseclass = oldobject.getXClass(context);
7204  0 String newPrefix = prefix + LOCAL_REFERENCE_SERIALIZER.serialize(absoluteClassReference) + "_" + nb;
7205  0 BaseObject newobject =
7206    (BaseObject) baseclass.fromMap(Util.getObject(context.getRequest(), newPrefix), oldobject);
7207  0 newobject.setNumber(oldobject.getNumber());
7208  0 newobject.setGuid(oldobject.getGuid());
7209  0 setXObject(nb, newobject);
7210   
7211  0 return newobject;
7212    }
7213   
7214    /**
7215    * @deprecated since 2.2M2 use {@link #updateXObjectFromRequest(EntityReference, String, int, XWikiContext)}
7216    */
 
7217  0 toggle @Deprecated
7218    public BaseObject updateObjectFromRequest(String className, String prefix, int num, XWikiContext context)
7219    throws XWikiException
7220    {
7221  0 return updateXObjectFromRequest(
7222    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()), prefix,
7223    num, context);
7224    }
7225   
7226    /**
7227    * Adds an object from an new object creation form.
7228    *
7229    * @since 2.2.3
7230    */
 
7231  0 toggle public List<BaseObject> updateXObjectsFromRequest(EntityReference classReference, XWikiContext context)
7232    throws XWikiException
7233    {
7234  0 return updateXObjectsFromRequest(classReference, "", context);
7235    }
7236   
7237    /**
7238    * @deprecated since 2.2M2 use {@link #updateXObjectsFromRequest(EntityReference, XWikiContext)}
7239    */
 
7240  0 toggle @Deprecated
7241    public List<BaseObject> updateObjectsFromRequest(String className, XWikiContext context) throws XWikiException
7242    {
7243  0 return updateObjectsFromRequest(className, "", context);
7244    }
7245   
7246    /**
7247    * Adds multiple objects from an new objects creation form.
7248    *
7249    * @since 2.2.3
7250    */
 
7251  0 toggle public List<BaseObject> updateXObjectsFromRequest(EntityReference classReference, String pref, XWikiContext context)
7252    throws XWikiException
7253    {
7254  0 DocumentReference absoluteClassReference = resolveClassReference(classReference);
7255  0 @SuppressWarnings("unchecked")
7256    Map<String, String[]> map = context.getRequest().getParameterMap();
7257  0 List<Integer> objectsNumberDone = new ArrayList<Integer>();
7258  0 List<BaseObject> objects = new ArrayList<BaseObject>();
7259  0 String start = pref + LOCAL_REFERENCE_SERIALIZER.serialize(absoluteClassReference) + "_";
7260   
7261  0 for (String name : map.keySet()) {
7262  0 if (name.startsWith(start)) {
7263  0 int pos = name.indexOf('_', start.length() + 1);
7264  0 String prefix = name.substring(0, pos);
7265  0 int num = Integer.decode(prefix.substring(prefix.lastIndexOf('_') + 1)).intValue();
7266  0 if (!objectsNumberDone.contains(Integer.valueOf(num))) {
7267  0 objectsNumberDone.add(Integer.valueOf(num));
7268  0 objects.add(updateXObjectFromRequest(classReference, pref, num, context));
7269    }
7270    }
7271    }
7272   
7273  0 return objects;
7274    }
7275   
7276    /**
7277    * @deprecated since 2.2M2 use {@link #updateXObjectsFromRequest(EntityReference, String, XWikiContext)}
7278    */
 
7279  0 toggle @Deprecated
7280    public List<BaseObject> updateObjectsFromRequest(String className, String pref, XWikiContext context)
7281    throws XWikiException
7282    {
7283  0 return updateXObjectsFromRequest(
7284    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()), pref,
7285    context);
7286    }
7287   
 
7288  0 toggle public boolean isAdvancedContent()
7289    {
7290  0 String[] matches = { "<%", "#set", "#include", "#if", "public class", "/* Advanced content */",
7291    "## Advanced content", "/* Programmatic content */", "## Programmatic content" };
7292  0 String content2 = getContent().toLowerCase();
7293  0 for (String match : matches) {
7294  0 if (content2.indexOf(match.toLowerCase()) != -1) {
7295  0 return true;
7296    }
7297    }
7298   
7299  0 if (HTML_TAG_PATTERN.matcher(content2).find()) {
7300  0 return true;
7301    }
7302   
7303  0 return false;
7304    }
7305   
 
7306  0 toggle public boolean isProgrammaticContent()
7307    {
7308  0 String[] matches = { "<%", "\\$xwiki.xWiki", "$xcontext.context", "$doc.document", "$xwiki.getXWiki()",
7309    "$xcontext.getContext()", "$doc.getDocument()", "WithProgrammingRights(", "/* Programmatic content */",
7310    "## Programmatic content", "$xwiki.search(", "$xwiki.createUser", "$xwiki.createNewWiki",
7311    "$xwiki.addToAllGroup", "$xwiki.sendMessage", "$xwiki.copyDocument", "$xwiki.copyWikiWeb",
7312    "$xwiki.copySpaceBetweenWikis", "$xwiki.parseGroovyFromString", "$doc.toXML()", "$doc.toXMLDocument()", };
7313  0 String content2 = getContent().toLowerCase();
7314  0 for (String match : matches) {
7315  0 if (content2.indexOf(match.toLowerCase()) != -1) {
7316  0 return true;
7317    }
7318    }
7319   
7320  0 return false;
7321    }
7322   
7323    /**
7324    * Remove an XObject from the document. The changes are not persisted until the document is saved.
7325    *
7326    * @param object the object to remove
7327    * @return {@code true} if the object was successfully removed, {@code false} if the object was not found in the
7328    * current document.
7329    * @since 2.2M1
7330    */
 
7331  110 toggle public boolean removeXObject(BaseObject object)
7332    {
7333  110 List<BaseObject> objects = this.xObjects.get(object.getXClassReference());
7334    // No objects at all, nothing to remove
7335  110 if (objects == null) {
7336  0 return false;
7337    }
7338    // Sometimes the object vector is wrongly indexed, meaning that objects are not at the right position
7339    // Check if the right object is in place
7340  110 int objectPosition = object.getNumber();
7341  110 if (objectPosition < objects.size()) {
7342  110 BaseObject storedObject = objects.get(objectPosition);
7343  110 if (storedObject == null || !storedObject.equals(object)) {
7344    // Try to find the correct position
7345  1 objectPosition = objects.indexOf(object);
7346    }
7347    } else {
7348    // The object position is greater than the array, that's invalid!
7349  0 objectPosition = -1;
7350    }
7351    // If the object is not in the document, simply ignore this request
7352  110 if (objectPosition < 0) {
7353  0 return false;
7354    }
7355    // We don't remove objects, but set null in their place, so that the object number corresponds to its position
7356    // in the vector
7357  110 objects.set(objectPosition, null);
7358    // Schedule the object for removal from the storage
7359  110 addXObjectToRemove(object);
7360   
7361  110 return true;
7362    }
7363   
7364    /**
7365    * Remove an XObject from the document. The changes are not persisted until the document is saved.
7366    *
7367    * @param object the object to remove
7368    * @return {@code true} if the object was successfully removed, {@code false} if the object was not found in the
7369    * current document.
7370    * @deprecated since 2.2M1, use {@link #removeXObject(com.xpn.xwiki.objects.BaseObject)} instead
7371    */
 
7372  10 toggle @Deprecated
7373    public boolean removeObject(BaseObject object)
7374    {
7375  10 return removeXObject(object);
7376    }
7377   
7378    /**
7379    * Remove all the objects of a given type (XClass) from the document. The object counter is left unchanged, so that
7380    * future objects will have new (different) numbers. However, on some storage engines the counter will be reset if
7381    * the document is removed from the cache and reloaded from the persistent storage.
7382    *
7383    * @param classReference The XClass reference of the XObjects to be removed.
7384    * @return {@code true} if the objects were successfully removed, {@code false} if no object from the target class
7385    * was in the current document.
7386    * @since 2.2M1
7387    */
 
7388  3 toggle public boolean removeXObjects(DocumentReference classReference)
7389    {
7390  3 List<BaseObject> objects = this.xObjects.get(classReference);
7391    // No objects at all, nothing to remove
7392  3 if (objects == null) {
7393  0 return false;
7394    }
7395    // Schedule the object for removal from the storage
7396  3 for (BaseObject object : objects) {
7397  5 if (object != null) {
7398  3 addXObjectToRemove(object);
7399    }
7400    }
7401    // Empty the vector, retaining its size
7402  3 int currentSize = objects.size();
7403  3 objects.clear();
7404  8 for (int i = 0; i < currentSize; i++) {
7405  5 objects.add(null);
7406    }
7407   
7408  3 return true;
7409    }
7410   
7411    /**
7412    * Remove all the objects of a given type (XClass) from the document. The object counter is left unchanged, so that
7413    * future objects will have new (different) numbers. However, on some storage engines the counter will be reset if
7414    * the document is removed from the cache and reloaded from the persistent storage.
7415    *
7416    * @param reference The XClass reference of the XObjects to be removed.
7417    * @return {@code true} if the objects were successfully removed, {@code false} if no object from the target class
7418    * was in the current document.
7419    * @since 5.0M1
7420    */
 
7421  2 toggle public boolean removeXObjects(EntityReference reference)
7422    {
7423  2 return removeXObjects(
7424    getCurrentReferenceDocumentReferenceResolver().resolve(reference, getDocumentReference()));
7425    }
7426   
7427    /**
7428    * Remove all the objects of a given type (XClass) from the document. The object counter is left unchanged, so that
7429    * future objects will have new (different) numbers. However, on some storage engines the counter will be reset if
7430    * the document is removed from the cache and reloaded from the persistent storage.
7431    *
7432    * @param className The class name of the objects to be removed.
7433    * @return {@code true} if the objects were successfully removed, {@code false} if no object from the target class
7434    * was in the current document.
7435    * @deprecated since 2.2M1 use {@link #removeXObjects(org.xwiki.model.reference.DocumentReference)} instead
7436    */
 
7437  1 toggle @Deprecated
7438    public boolean removeObjects(String className)
7439    {
7440  1 return removeXObjects(resolveClassReference(className));
7441    }
7442   
7443    /**
7444    * Get the top sections contained in the document.
7445    * <p>
7446    * The section are filtered by xwiki.section.depth property on the maximum depth of the sections to return. This
7447    * method is usually used to get "editable" sections.
7448    *
7449    * @return the sections in the current document
7450    */
 
7451  23 toggle public List<DocumentSection> getSections() throws XWikiException
7452    {
7453  23 if (is10Syntax()) {
7454  19 return getSections10();
7455    } else {
7456  4 List<DocumentSection> splitSections = new ArrayList<DocumentSection>();
7457  4 List<HeaderBlock> headers = getFilteredHeaders();
7458   
7459  4 int sectionNumber = 1;
7460  4 for (HeaderBlock header : headers) {
7461    // put -1 as index since there is no way to get the position of the header in the source
7462  9 int documentSectionIndex = -1;
7463   
7464    // Need to do the same thing than 1.0 content here
7465  9 String documentSectionLevel = StringUtils.repeat("1.", header.getLevel().getAsInt() - 1) + "1";
7466   
7467  9 DocumentSection docSection = new DocumentSection(sectionNumber++, documentSectionIndex,
7468    documentSectionLevel, renderXDOM(new XDOM(header.getChildren()), getSyntax()));
7469  9 splitSections.add(docSection);
7470    }
7471   
7472  4 return splitSections;
7473    }
7474    }
7475   
7476    /**
7477    * Get XWiki context from execution context.
7478    *
7479    * @return the XWiki context for the current thread
7480    */
 
7481  6207 toggle private XWikiContext getXWikiContext()
7482    {
7483  6207 Provider<XWikiContext> xcontextProvider = Utils.getComponent(XWikiContext.TYPE_PROVIDER);
7484   
7485  6207 if (xcontextProvider != null) {
7486  6207 return xcontextProvider.get();
7487    }
7488   
7489  0 return null;
7490    }
7491   
7492    /**
7493    * Filter the headers from a document XDOM based on xwiki.section.depth property from xwiki.cfg file.
7494    *
7495    * @return the filtered headers
7496    */
 
7497  12 toggle private List<HeaderBlock> getFilteredHeaders()
7498    {
7499  12 List<HeaderBlock> filteredHeaders = new ArrayList<HeaderBlock>();
7500   
7501    // Get the maximum header level
7502  12 int sectionDepth = 2;
7503  12 XWikiContext context = getXWikiContext();
7504  12 if (context != null) {
7505  12 sectionDepth = (int) context.getWiki().getSectionEditingDepth();
7506    }
7507   
7508    // Get the headers.
7509    //
7510    // Note that we need to only take into account SectionBlock that are children of other SectionBlocks so that
7511    // we are in sync with the section editing buttons added in xwiki.js. Being able to section edit any heading is
7512    // too complex. For example if you have (in XWiki Syntax 2.0):
7513    // = Heading1 =
7514    // para1
7515    // == Heading2 ==
7516    // para2
7517    // (((
7518    // == Heading3 ==
7519    // para3
7520    // (((
7521    // == Heading4 ==
7522    // para4
7523    // )))
7524    // )))
7525    // == Heading5 ==
7526    // para5
7527    //
7528    // Then if we were to support editing "Heading4", its content would be:
7529    // para4
7530    // )))
7531    // )))
7532    //
7533    // Which obviously is not correct...
7534   
7535  12 final XDOM xdom = getXDOM();
7536  12 if (!xdom.getChildren().isEmpty()) {
7537  12 Block currentBlock = xdom.getChildren().get(0);
7538  129 while (currentBlock != null) {
7539  117 if (currentBlock instanceof SectionBlock) {
7540    // The next children block is a HeaderBlock but we check to be on the safe side...
7541  35 Block nextChildrenBlock = currentBlock.getChildren().get(0);
7542  35 if (nextChildrenBlock instanceof HeaderBlock) {
7543  35 HeaderBlock headerBlock = (HeaderBlock) nextChildrenBlock;
7544  35 if (headerBlock.getLevel().getAsInt() <= sectionDepth) {
7545  32 filteredHeaders.add(headerBlock);
7546    }
7547    }
7548  35 currentBlock = nextChildrenBlock;
7549    } else {
7550  82 Block nextSibling = currentBlock.getNextSibling();
7551  82 if (nextSibling == null) {
7552  18 currentBlock = currentBlock.getParent();
7553  59 while (currentBlock != null) {
7554  47 if (currentBlock.getNextSibling() != null) {
7555  6 currentBlock = currentBlock.getNextSibling();
7556  6 break;
7557    }
7558  41 currentBlock = currentBlock.getParent();
7559    }
7560    } else {
7561  64 currentBlock = nextSibling;
7562    }
7563    }
7564    }
7565    }
7566   
7567  12 return filteredHeaders;
7568    }
7569   
7570    /**
7571    * @return the sections in the current document
7572    */
 
7573  19 toggle private List<DocumentSection> getSections10()
7574    {
7575    // Pattern to match the title. Matches only level 1 and level 2 headings.
7576  19 Pattern headingPattern = Pattern.compile("^[ \\t]*+(1(\\.1){0,1}+)[ \\t]++(.++)$", Pattern.MULTILINE);
7577  19 Matcher matcher = headingPattern.matcher(getContent());
7578  19 List<DocumentSection> splitSections = new ArrayList<DocumentSection>();
7579  19 int sectionNumber = 0;
7580    // find title to split
7581  70 while (matcher.find()) {
7582  51 ++sectionNumber;
7583  51 String sectionLevel = matcher.group(1);
7584  51 String sectionTitle = matcher.group(3);
7585  51 int sectionIndex = matcher.start();
7586    // Initialize a documentSection object.
7587  51 DocumentSection docSection = new DocumentSection(sectionNumber, sectionIndex, sectionLevel, sectionTitle);
7588    // Add the document section to list.
7589  51 splitSections.add(docSection);
7590    }
7591   
7592  19 return splitSections;
7593    }
7594   
7595    /**
7596    * Return a Document section with parameter is sectionNumber.
7597    *
7598    * @param sectionNumber the index (+1) of the section in the list of all sections in the document.
7599    * @return
7600    * @throws XWikiException error when extracting sections from document
7601    */
 
7602  6 toggle public DocumentSection getDocumentSection(int sectionNumber) throws XWikiException
7603    {
7604    // return a document section according to section number
7605  6 return getSections().get(sectionNumber - 1);
7606    }
7607   
7608    /**
7609    * Return the content of a section.
7610    *
7611    * @param sectionNumber the index (+1) of the section in the list of all sections in the document.
7612    * @return the content of a section or null if the section can't be found.
7613    * @throws XWikiException error when trying to extract section content
7614    */
 
7615  13 toggle public String getContentOfSection(int sectionNumber) throws XWikiException
7616    {
7617  13 String content = null;
7618   
7619  13 if (is10Syntax()) {
7620  8 content = getContentOfSection10(sectionNumber);
7621    } else {
7622  5 List<HeaderBlock> headers = getFilteredHeaders();
7623   
7624  5 if (headers.size() >= sectionNumber) {
7625  5 SectionBlock section = headers.get(sectionNumber - 1).getSection();
7626  5 content = renderXDOM(new XDOM(Collections.<Block>singletonList(section)), getSyntax());
7627    }
7628    }
7629   
7630  13 return content;
7631    }
7632   
7633    /**
7634    * Return the content of a section.
7635    *
7636    * @param sectionNumber the index (+1) of the section in the list of all sections in the document.
7637    * @return the content of a section
7638    * @throws XWikiException error when trying to extract section content
7639    */
 
7640  8 toggle private String getContentOfSection10(int sectionNumber) throws XWikiException
7641    {
7642  8 List<DocumentSection> splitSections = getSections();
7643  8 int indexEnd = 0;
7644    // get current section
7645  8 DocumentSection section = splitSections.get(sectionNumber - 1);
7646  8 int indexStart = section.getSectionIndex();
7647  8 String sectionLevel = section.getSectionLevel();
7648    // Determine where this section ends, which is at the start of the next section of the
7649    // same or a higher level.
7650  11 for (int i = sectionNumber; i < splitSections.size(); i++) {
7651  7 DocumentSection nextSection = splitSections.get(i);
7652  7 String nextLevel = nextSection.getSectionLevel();
7653  7 if (sectionLevel.equals(nextLevel) || sectionLevel.length() > nextLevel.length()) {
7654  4 indexEnd = nextSection.getSectionIndex();
7655  4 break;
7656    }
7657    }
7658  8 String sectionContent = null;
7659  8 if (indexStart < 0) {
7660  0 indexStart = 0;
7661    }
7662   
7663  8 if (indexEnd == 0) {
7664  4 sectionContent = getContent().substring(indexStart);
7665    } else {
7666  4 sectionContent = getContent().substring(indexStart, indexEnd);
7667    }
7668   
7669  8 return sectionContent;
7670    }
7671   
7672    /**
7673    * Update a section content in document.
7674    *
7675    * @param sectionNumber the index (starting at 1) of the section in the list of all sections in the document.
7676    * @param newSectionContent the new section content.
7677    * @return the new document content.
7678    * @throws XWikiException error when updating content
7679    */
 
7680  4 toggle public String updateDocumentSection(int sectionNumber, String newSectionContent) throws XWikiException
7681    {
7682  4 String content;
7683  4 if (is10Syntax()) {
7684  1 content = updateDocumentSection10(sectionNumber, newSectionContent);
7685    } else {
7686    // Get the current section block
7687  3 HeaderBlock header = getFilteredHeaders().get(sectionNumber - 1);
7688   
7689  3 XDOM xdom = (XDOM) header.getRoot();
7690   
7691    // newSectionContent -> Blocks
7692  3 List<Block> blocks = parseContent(newSectionContent).getChildren();
7693  3 int sectionLevel = header.getLevel().getAsInt();
7694  4 for (int level = 1; level < sectionLevel && blocks.size() == 1
7695    && blocks.get(0) instanceof SectionBlock; ++level) {
7696  1 blocks = blocks.get(0).getChildren();
7697    }
7698   
7699    // replace old current SectionBlock with new Blocks
7700  3 Block section = header.getSection();
7701  3 section.getParent().replaceChild(blocks, section);
7702   
7703    // render back XDOM to document's content syntax
7704  3 content = renderXDOM(xdom, getSyntax());
7705    }
7706   
7707  4 return content;
7708    }
7709   
7710    /**
7711    * Update a section content in document.
7712    *
7713    * @param sectionNumber the index (+1) of the section in the list of all sections in the document.
7714    * @param newSectionContent the new section content.
7715    * @return the new document content.
7716    * @throws XWikiException error when updating document content with section content
7717    */
 
7718  1 toggle private String updateDocumentSection10(int sectionNumber, String newSectionContent) throws XWikiException
7719    {
7720  1 StringBuilder newContent = new StringBuilder();
7721    // get document section that will be edited
7722  1 DocumentSection docSection = getDocumentSection(sectionNumber);
7723  1 int numberOfSections = getSections().size();
7724  1 int indexSection = docSection.getSectionIndex();
7725  1 if (numberOfSections == 1) {
7726    // there is only a sections in document
7727  0 String contentBegin = getContent().substring(0, indexSection);
7728  0 newContent = newContent.append(contentBegin).append(newSectionContent);
7729  0 return newContent.toString();
7730  1 } else if (sectionNumber == numberOfSections) {
7731    // edit lastest section that doesn't contain subtitle
7732  1 String contentBegin = getContent().substring(0, indexSection);
7733  1 newContent = newContent.append(contentBegin).append(newSectionContent);
7734  1 return newContent.toString();
7735    } else {
7736  0 String sectionLevel = docSection.getSectionLevel();
7737  0 int nextSectionIndex = 0;
7738    // get index of next section
7739  0 for (int i = sectionNumber; i < numberOfSections; i++) {
7740  0 DocumentSection nextSection = getDocumentSection(i + 1); // get next section
7741  0 String nextSectionLevel = nextSection.getSectionLevel();
7742  0 if (sectionLevel.equals(nextSectionLevel)) {
7743  0 nextSectionIndex = nextSection.getSectionIndex();
7744  0 break;
7745  0 } else if (sectionLevel.length() > nextSectionLevel.length()) {
7746  0 nextSectionIndex = nextSection.getSectionIndex();
7747  0 break;
7748    }
7749    }
7750   
7751  0 if (nextSectionIndex == 0) {// edit the last section
7752  0 newContent = newContent.append(getContent().substring(0, indexSection)).append(newSectionContent);
7753  0 return newContent.toString();
7754    } else {
7755  0 String contentAfter = getContent().substring(nextSectionIndex);
7756  0 String contentBegin = getContent().substring(0, indexSection);
7757  0 newContent = newContent.append(contentBegin).append(newSectionContent).append(contentAfter);
7758    }
7759   
7760  0 return newContent.toString();
7761    }
7762    }
7763   
7764    /**
7765    * Computes a document hash, taking into account all document data: content, objects, attachments, metadata... TODO:
7766    * cache the hash value, update only on modification.
7767    */
 
7768  0 toggle public String getVersionHashCode(XWikiContext context)
7769    {
7770  0 MessageDigest md5 = null;
7771   
7772  0 try {
7773  0 md5 = MessageDigest.getInstance("MD5");
7774    } catch (NoSuchAlgorithmException ex) {
7775  0 LOGGER.error("Cannot create MD5 object", ex);
7776  0 return hashCode() + "";
7777    }
7778   
7779  0 try {
7780  0 String valueBeforeMD5 = toXML(true, false, true, false, context);
7781  0 md5.update(valueBeforeMD5.getBytes());
7782   
7783  0 byte[] array = md5.digest();
7784  0 StringBuilder sb = new StringBuilder();
7785  0 for (byte element : array) {
7786  0 int b = element & 0xFF;
7787  0 if (b < 0x10) {
7788  0 sb.append('0');
7789    }
7790  0 sb.append(Integer.toHexString(b));
7791    }
7792   
7793  0 return sb.toString();
7794    } catch (Exception ex) {
7795  0 LOGGER.error("Exception while computing document hash", ex);
7796    }
7797   
7798  0 return hashCode() + "";
7799    }
7800   
 
7801  0 toggle public static String getInternalPropertyName(String propname, XWikiContext context)
7802    {
7803  0 ContextualLocalizationManager localizationManager = Utils.getComponent(ContextualLocalizationManager.class);
7804  0 String cpropname = StringUtils.capitalize(propname);
7805   
7806  0 return localizationManager == null ? cpropname : localizationManager.getTranslationPlain(cpropname);
7807    }
7808   
 
7809  0 toggle public String getInternalProperty(String propname)
7810    {
7811  0 String methodName = "get" + StringUtils.capitalize(propname);
7812  0 try {
7813  0 Method method = getClass().getDeclaredMethod(methodName, (Class[]) null);
7814  0 return (String) method.invoke(this, (Object[]) null);
7815    } catch (Exception e) {
7816  0 return null;
7817    }
7818    }
7819   
 
7820  239149 toggle public String getCustomClass()
7821    {
7822  239133 if (this.customClass == null) {
7823  1559 return "";
7824    }
7825   
7826  237571 return this.customClass;
7827    }
7828   
 
7829  42641 toggle public void setCustomClass(String customClass)
7830    {
7831  42642 this.customClass = customClass;
7832  42639 setMetaDataDirty(true);
7833    }
7834   
 
7835  42633 toggle public void setValidationScript(String validationScript)
7836    {
7837  42637 this.validationScript = validationScript;
7838   
7839  42635 setMetaDataDirty(true);
7840    }
7841   
 
7842  46028 toggle public String getValidationScript()
7843    {
7844  46028 if (this.validationScript == null) {
7845  16363 return "";
7846    } else {
7847  29669 return this.validationScript;
7848    }
7849    }
7850   
 
7851  56634 toggle public String getComment()
7852    {
7853  56634 if (this.comment == null) {
7854  0 return "";
7855    }
7856   
7857  56632 return this.comment;
7858    }
7859   
 
7860  47093 toggle public void setComment(String comment)
7861    {
7862  47096 this.comment = comment;
7863    }
7864   
 
7865  46301 toggle public boolean isMinorEdit()
7866    {
7867  46302 return this.isMinorEdit;
7868    }
7869   
 
7870  45762 toggle public void setMinorEdit(boolean isMinor)
7871    {
7872  45762 this.isMinorEdit = isMinor;
7873    }
7874   
7875    // methods for easy table update. It is need only for hibernate.
7876    // when hibernate update old database without minorEdit field, hibernate will create field with
7877    // null in despite of notnull in hbm.
7878    // (http://opensource.atlassian.com/projects/hibernate/browse/HB-1151)
7879    // so minorEdit will be null for old documents. But hibernate can't convert null to boolean.
7880    // so we need convert Boolean to boolean
 
7881  7972 toggle protected Boolean getMinorEdit1()
7882    {
7883  7972 return Boolean.valueOf(isMinorEdit());
7884    }
7885   
 
7886  3641 toggle protected void setMinorEdit1(Boolean isMinor)
7887    {
7888  3643 this.isMinorEdit = (isMinor != null && isMinor.booleanValue());
7889    }
7890   
7891    /**
7892    * Create, add and return a new object with the provided class.
7893    * <p>
7894    * Note that absolute reference are not supported for xclasses which mean that the wiki part (whatever the wiki is)
7895    * of the reference will be systematically removed.
7896    *
7897    * @param classReference the reference of the class
7898    * @param context the XWiki context
7899    * @return the newly created object
7900    * @throws XWikiException error when creating the new object
7901    * @since 2.2.3
7902    */
 
7903  1451 toggle public BaseObject newXObject(EntityReference classReference, XWikiContext context) throws XWikiException
7904    {
7905  1451 int nb = createXObject(classReference, context);
7906  1451 return getXObject(resolveClassReference(classReference), nb);
7907    }
7908   
7909    /**
7910    * @deprecated since 2.2M2 use {@link #newXObject(EntityReference, XWikiContext)}
7911    */
 
7912  59 toggle @Deprecated
7913    public BaseObject newObject(String className, XWikiContext context) throws XWikiException
7914    {
7915  59 return newXObject(
7916    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()),
7917    context);
7918    }
7919   
7920    /**
7921    * @since 2.2M2
7922    */
 
7923  0 toggle public BaseObject getXObject(DocumentReference classReference, boolean create, XWikiContext context)
7924    {
7925  0 try {
7926  0 BaseObject obj = getXObject(classReference);
7927   
7928  0 if ((obj == null) && create) {
7929  0 return newXObject(classReference, context);
7930    }
7931   
7932  0 if (obj == null) {
7933  0 return null;
7934    } else {
7935  0 return obj;
7936    }
7937    } catch (Exception e) {
7938  0 return null;
7939    }
7940    }
7941   
7942    /**
7943    * @since 3.4M1
7944    */
 
7945  7890 toggle public BaseObject getXObject(EntityReference classReference, boolean create, XWikiContext context)
7946    {
7947  7891 try {
7948  7891 BaseObject obj = getXObject(classReference);
7949   
7950  7890 if ((obj == null) && create) {
7951  14 return newXObject(classReference, context);
7952    }
7953   
7954  7876 if (obj == null) {
7955  6270 return null;
7956    } else {
7957  1606 return obj;
7958    }
7959    } catch (Exception e) {
7960  0 return null;
7961    }
7962    }
7963   
7964    /**
7965    * @deprecated since 2.2M2 use {@link #getXObject(DocumentReference, boolean, XWikiContext)}
7966    */
 
7967  7871 toggle @Deprecated
7968    public BaseObject getObject(String className, boolean create, XWikiContext context)
7969    {
7970  7872 return getXObject(
7971    getXClassEntityReferenceResolver().resolve(className, EntityType.DOCUMENT, getDocumentReference()), create,
7972    context);
7973    }
7974   
 
7975  2 toggle public boolean validate(XWikiContext context) throws XWikiException
7976    {
7977  2 return validate(null, context);
7978    }
7979   
 
7980  2 toggle public boolean validate(String[] classNames, XWikiContext context) throws XWikiException
7981    {
7982  2 boolean isValid = true;
7983  2 if ((classNames == null) || (classNames.length == 0)) {
7984  2 for (DocumentReference classReference : getXObjects().keySet()) {
7985  2 BaseClass bclass = context.getWiki().getXClass(classReference, context);
7986  2 List<BaseObject> objects = getXObjects(classReference);
7987  2 for (BaseObject obj : objects) {
7988  4 if (obj != null) {
7989  4 isValid &= bclass.validateObject(obj, context);
7990    }
7991    }
7992    }
7993    } else {
7994  0 for (String className : classNames) {
7995  0 List<BaseObject> objects = getXObjects(getCurrentMixedDocumentReferenceResolver().resolve(className));
7996  0 if (objects != null) {
7997  0 for (BaseObject obj : objects) {
7998  0 if (obj != null) {
7999  0 BaseClass bclass = obj.getXClass(context);
8000  0 isValid &= bclass.validateObject(obj, context);
8001    }
8002    }
8003    }
8004    }
8005    }
8006   
8007  2 String validationScript = "";
8008  2 XWikiRequest req = context.getRequest();
8009  2 if (req != null) {
8010  0 validationScript = req.get("xvalidation");
8011    }
8012   
8013  2 if ((validationScript == null) || (validationScript.trim().equals(""))) {
8014  2 validationScript = getValidationScript();
8015    }
8016   
8017  2 if ((validationScript != null) && (!validationScript.trim().equals(""))) {
8018  2 isValid &= executeValidationScript(context, validationScript);
8019    }
8020   
8021  2 return isValid;
8022    }
8023   
 
8024  16939 toggle public static void backupContext(Map<String, Object> backup, XWikiContext context)
8025    {
8026    // The XWiki Context isn't recreated when the Execution Context is cloned so we have to backup some of its data.
8027    // Backup the current document on the XWiki Context.
8028  16940 backup.put("doc", context.getDoc());
8029   
8030  16938 backup.put("cdoc", context.get("cdoc"));
8031  16941 backup.put("tdoc", context.get("tdoc"));
8032   
8033    // Backup the secure document
8034  16937 backup.put(CKEY_SDOC, context.get(CKEY_SDOC));
8035   
8036    // Clone the Execution Context to provide isolation. The clone will have a new Velocity and Script Context.
8037  16940 Execution execution = Utils.getComponent(Execution.class);
8038  16941 try {
8039  16941 execution.pushContext(Utils.getComponent(ExecutionContextManager.class).clone(execution.getContext()));
8040    } catch (ExecutionContextException e) {
8041  0 throw new RuntimeException("Failed to clone the Execution Context", e);
8042    }
8043   
8044    // Bridge with old XWiki Context, required for legacy code.
8045  16941 execution.getContext().setProperty(XWikiContext.EXECUTIONCONTEXT_KEY, context);
8046    }
8047   
 
8048  16937 toggle public static void restoreContext(Map<String, Object> backup, XWikiContext context)
8049    {
8050    // Restore the Execution Context. This will also restore the previous Velocity and Script Context.
8051  16933 Execution execution = Utils.getComponent(Execution.class);
8052  16941 execution.popContext();
8053   
8054    // Restore the current document on the XWiki Context.
8055  16937 context.setDoc((XWikiDocument) backup.get("doc"));
8056   
8057  16934 context.put("cdoc", backup.get("cdoc"));
8058  16939 context.put("tdoc", backup.get("tdoc"));
8059   
8060    // Restore the secure document
8061  16939 context.put(CKEY_SDOC, backup.get(CKEY_SDOC));
8062    }
8063   
 
8064  16935 toggle public void setAsContextDoc(XWikiContext context)
8065    {
8066  16936 context.setDoc(this);
8067  16935 context.remove("cdoc");
8068  16935 context.remove("tdoc");
8069   
8070    // Get rid of secure document (so that it fallback on context document)
8071  16936 context.remove(CKEY_SDOC);
8072    }
8073   
8074    /**
8075    * @return the String representation of the previous version of this document or null if this is the first version.
8076    */
 
8077  7 toggle public String getPreviousVersion()
8078    {
8079  7 XWikiDocumentArchive archive = loadDocumentArchive();
8080  7 if (archive != null) {
8081  7 Version prevVersion = archive.getPrevVersion(getRCSVersion());
8082  7 if (prevVersion != null) {
8083  4 return prevVersion.toString();
8084    }
8085    }
8086  3 return null;
8087    }
8088   
 
8089  20713 toggle @Override
8090    public String toString()
8091    {
8092  20713 return getFullName();
8093    }
8094   
8095    /**
8096    * Indicates whether the document should be 'hidden' or not, meaning that it should not be returned in public search
8097    * results.
8098    *
8099    * @param hidden The new value of the {@link #hidden} property.
8100    */
 
8101  43880 toggle public void setHidden(Boolean hidden)
8102    {
8103  43884 if (hidden == null) {
8104  0 this.hidden = false;
8105    } else {
8106  43881 this.hidden = hidden;
8107    }
8108    }
8109   
8110    /**
8111    * Indicates whether the document is 'hidden' or not, meaning that it should not be returned in public search
8112    * results.
8113    *
8114    * @return <code>true</code> if the document is hidden and does not appear among the results of
8115    * {@link com.xpn.xwiki.api.XWiki#searchDocuments(String)}, <code>false</code> otherwise.
8116    */
 
8117  58666 toggle public Boolean isHidden()
8118    {
8119  58662 return this.hidden;
8120    }
8121   
8122    /**
8123    * Convert the current document content from its current syntax to the new syntax passed as parameter.
8124    *
8125    * @param targetSyntaxId the syntax to convert to (e.g. {@code xwiki/2.0}, {@code xhtml/1.0}, etc)
8126    * @throws XWikiException if an exception occurred during the conversion process
8127    */
 
8128  1 toggle public void convertSyntax(String targetSyntaxId, XWikiContext context) throws XWikiException
8129    {
8130  1 try {
8131  1 convertSyntax(getSyntaxFactory().createSyntaxFromIdString(targetSyntaxId), context);
8132    } catch (Exception e) {
8133  0 throw new XWikiException(XWikiException.MODULE_XWIKI_RENDERING, XWikiException.ERROR_XWIKI_UNKNOWN,
8134    "Failed to convert document to syntax [" + targetSyntaxId + "]", e);
8135    }
8136    }
8137   
8138    /**
8139    * Convert the current document content from its current syntax to the new syntax passed as parameter.
8140    *
8141    * @param targetSyntax the syntax to convert to (e.g. {@code xwiki/2.0}, {@code xhtml/1.0}, etc)
8142    * @throws XWikiException if an exception occurred during the conversion process
8143    */
 
8144  1 toggle public void convertSyntax(Syntax targetSyntax, XWikiContext context) throws XWikiException
8145    {
8146    // convert content
8147  1 setContent(performSyntaxConversion(getContent(), getDocumentReference(), getSyntax(), targetSyntax));
8148   
8149    // convert objects
8150  1 Map<DocumentReference, List<BaseObject>> objectsByClass = getXObjects();
8151   
8152  1 for (List<BaseObject> objects : objectsByClass.values()) {
8153  1 for (BaseObject bobject : objects) {
8154  2 if (bobject != null) {
8155  2 BaseClass bclass = bobject.getXClass(context);
8156  2 for (Object fieldClass : bclass.getProperties()) {
8157  14 if (fieldClass instanceof TextAreaClass && ((TextAreaClass) fieldClass).isWikiContent()) {
8158  2 TextAreaClass textAreaClass = (TextAreaClass) fieldClass;
8159  2 LargeStringProperty field = (LargeStringProperty) bobject.getField(textAreaClass.getName());
8160   
8161  2 if (field != null) {
8162  2 field.setValue(performSyntaxConversion(field.getValue(), getDocumentReference(),
8163    getSyntax(), targetSyntax));
8164    }
8165    }
8166    }
8167    }
8168    }
8169    }
8170   
8171    // change syntax
8172  1 setSyntax(targetSyntax);
8173    }
8174   
8175    /**
8176    * NOTE: This method caches the XDOM and returns a clone that can be safely modified.
8177    *
8178    * @return the XDOM corresponding to the document's string content
8179    */
 
8180  42549 toggle @Override
8181    public XDOM getXDOM()
8182    {
8183  42555 if (this.xdomCache == null) {
8184  13347 try {
8185  13348 this.xdomCache = parseContent(getContent());
8186    } catch (XWikiException e) {
8187  4 ErrorBlockGenerator errorBlockGenerator = Utils.getComponent(ErrorBlockGenerator.class);
8188  4 return new XDOM(errorBlockGenerator.generateErrorBlocks("Failed to render content", e, false));
8189    }
8190    }
8191   
8192  42546 return this.xdomCache.clone();
8193    }
8194   
8195    /**
8196    * @return true if the document has a xwiki/1.0 syntax content
8197    */
 
8198  38196 toggle public boolean is10Syntax()
8199    {
8200  38199 return is10Syntax(getSyntaxId());
8201    }
8202   
8203    /**
8204    * @return true if the document has a xwiki/1.0 syntax content
8205    */
 
8206  41104 toggle public boolean is10Syntax(String syntaxId)
8207    {
8208  41104 return Syntax.XWIKI_1_0.toIdString().equalsIgnoreCase(syntaxId);
8209    }
8210   
 
8211  1307795 toggle private void init(DocumentReference reference)
8212    {
8213    // if the passed reference is null consider it points to the default reference
8214  1307769 if (reference == null) {
8215  21892 setDocumentReference(
8216    Utils.<Provider<DocumentReference>>getComponent(DocumentReference.TYPE_PROVIDER).get());
8217    } else {
8218  1285867 setDocumentReference(reference);
8219    }
8220   
8221  1307738 this.updateDate = new Date();
8222  1307803 this.updateDate.setTime((this.updateDate.getTime() / 1000) * 1000);
8223  1307749 this.contentUpdateDate = new Date();
8224  1307780 this.contentUpdateDate.setTime((this.contentUpdateDate.getTime() / 1000) * 1000);
8225  1307789 this.creationDate = new Date();
8226  1307766 this.creationDate.setTime((this.creationDate.getTime() / 1000) * 1000);
8227  1307757 this.content = "";
8228  1307752 this.format = "";
8229  1307738 this.locale = Locale.ROOT;
8230  1307727 this.defaultLocale = Locale.ROOT;
8231  1307735 this.customClass = "";
8232  1307732 this.comment = "";
8233   
8234    // Note: As there's no notion of an Empty document we don't set the original document
8235    // field. Thus getOriginalDocument() may return null.
8236    }
8237   
 
8238  2 toggle private boolean executeValidationScript(XWikiContext context, String validationScript)
8239    {
8240  2 try {
8241  2 ContextualAuthorizationManager authorization = Utils.getComponent(ContextualAuthorizationManager.class);
8242  2 DocumentReference validationScriptReference =
8243    getCurrentDocumentReferenceResolver().resolve(validationScript, getDocumentReference());
8244   
8245    // Make sure target document is allowed to execute Groovy
8246    // TODO: this check should probably be right in XWiki#parseGroovyFromPage
8247  2 authorization.checkAccess(Right.PROGRAM, validationScriptReference);
8248   
8249  1 XWikiValidationInterface validObject =
8250    (XWikiValidationInterface) context.getWiki().parseGroovyFromPage(validationScript, context);
8251   
8252  1 return validObject.validateDocument(this, context);
8253    } catch (Throwable e) {
8254  1 XWikiValidationStatus.addExceptionToContext(getFullName(), "", e, context);
8255  1 return false;
8256    }
8257    }
8258   
8259    /**
8260    * Convert the passed content from the passed syntax to the passed new syntax.
8261    *
8262    * @param content the content to convert
8263    * @param source the reference to where the content comes from (eg document reference)
8264    * @param currentSyntaxId the syntax of the current content to convert
8265    * @param targetSyntax the new syntax after the conversion
8266    * @return the converted content in the new syntax
8267    * @throws XWikiException if an exception occurred during the conversion process
8268    * @since 2.4M2
8269    */
 
8270  3 toggle private static String performSyntaxConversion(String content, DocumentReference source, Syntax currentSyntaxId,
8271    Syntax targetSyntax) throws XWikiException
8272    {
8273  3 try {
8274  3 XDOM dom = parseContent(currentSyntaxId, content, source);
8275  3 return performSyntaxConversion(dom, targetSyntax, null);
8276    } catch (Exception e) {
8277  0 throw new XWikiException(XWikiException.MODULE_XWIKI_RENDERING, XWikiException.ERROR_XWIKI_UNKNOWN,
8278    "Failed to convert document to syntax [" + targetSyntax + "]", e);
8279    }
8280    }
8281   
8282    /**
8283    * Convert the passed content from the passed syntax to the passed new syntax.
8284    *
8285    * @param content the XDOM content to convert, the XDOM can be modified during the transformation
8286    * @param targetSyntax the new syntax after the conversion
8287    * @param txContext the context when Transformation are executed or null if transformation shouldn't be executed
8288    * @return the converted content in the new syntax
8289    * @throws XWikiException if an exception occurred during the conversion process
8290    * @since 2.4M2
8291    */
 
8292  3 toggle private static String performSyntaxConversion(XDOM content, Syntax targetSyntax, TransformationContext txContext)
8293    throws XWikiException
8294    {
8295  3 try {
8296  3 if (txContext != null) {
8297    // Transform XDOM
8298  0 TransformationManager transformations = Utils.getComponent(TransformationManager.class);
8299  0 if (txContext.getXDOM() == null) {
8300  0 txContext.setXDOM(content);
8301    }
8302  0 try {
8303  0 transformations.performTransformations(content, txContext);
8304    } catch (TransformationException te) {
8305    // An error happened during one of the transformations. Since the error has been logged
8306    // continue
8307    // TODO: We should have a visual clue for the user in the future to let him know something
8308    // didn't work as expected.
8309    }
8310    }
8311   
8312    // Render XDOM
8313  3 return renderXDOM(content, targetSyntax);
8314    } catch (Exception e) {
8315  0 throw new XWikiException(XWikiException.MODULE_XWIKI_RENDERING, XWikiException.ERROR_XWIKI_UNKNOWN,
8316    "Failed to convert document to syntax [" + targetSyntax + "]", e);
8317    }
8318    }
8319   
8320    /**
8321    * Render privided XDOM into content of the provided syntax identifier.
8322    *
8323    * @param content the XDOM content to render
8324    * @param targetSyntax the syntax identifier of the rendered content
8325    * @return the rendered content
8326    * @throws XWikiException if an exception occurred during the rendering process
8327    */
 
8328  25922 toggle protected static String renderXDOM(XDOM content, Syntax targetSyntax) throws XWikiException
8329    {
8330  25921 try {
8331  25919 BlockRenderer renderer = Utils.getComponent(BlockRenderer.class, targetSyntax.toIdString());
8332  25924 WikiPrinter printer = new DefaultWikiPrinter();
8333  25923 renderer.render(content, printer);
8334  25924 return printer.toString();
8335    } catch (Exception e) {
8336  0 throw new XWikiException(XWikiException.MODULE_XWIKI_RENDERING, XWikiException.ERROR_XWIKI_UNKNOWN,
8337    "Failed to render document to syntax [" + targetSyntax + "]", e);
8338    }
8339    }
8340   
 
8341  13350 toggle private XDOM parseContent(String content) throws XWikiException
8342    {
8343  13351 return parseContent(getSyntax(), content, getDocumentReference());
8344    }
8345   
8346    /**
8347    * @param source the reference to where the content comes from (eg document reference)
8348    */
 
8349  13348 toggle private static XDOM parseContent(Syntax syntax, String content, DocumentReference source) throws XWikiException
8350    {
8351  13349 ContentParser parser = Utils.getComponent(ContentParser.class);
8352   
8353  13354 try {
8354  13354 return parser.parse(content, syntax, source);
8355    } catch (MissingParserException e) {
8356  4 throw new XWikiException(XWikiException.MODULE_XWIKI_RENDERING, XWikiException.ERROR_XWIKI_UNKNOWN,
8357    "Failed to find a parser for syntax [" + syntax.toIdString() + "]", e);
8358    } catch (ParseException e) {
8359  0 throw new XWikiException(XWikiException.MODULE_XWIKI_RENDERING, XWikiException.ERROR_XWIKI_UNKNOWN,
8360    "Failed to parse content of syntax [" + syntax.toIdString() + "]", e);
8361    }
8362    }
8363   
 
8364  17045 toggle private Syntax getDefaultDocumentSyntax()
8365    {
8366    // If there's no parser available for the specified syntax default to XWiki 2.1 syntax
8367  17044 Syntax syntax = Utils.getComponent(CoreConfiguration.class).getDefaultDocumentSyntax();
8368   
8369  17046 if (syntax == null || !Utils.getComponentManager().hasComponent(Parser.class, syntax.toIdString())) {
8370  85 LOGGER.warn("Failed to find parser for the default syntax [{}]. Defaulting to xwiki/2.1 syntax.", syntax);
8371  85 syntax = Syntax.XWIKI_2_1;
8372    }
8373   
8374  17042 return syntax;
8375    }
8376   
8377    /**
8378    * Backward-compatibility method to use in order to resolve a class reference passed as a String into a
8379    * DocumentReference proper.
8380    *
8381    * @return the resolved class reference but using this document's wiki if the passed String doesn't specify a wiki,
8382    * the "XWiki" space if the passed String doesn't specify a space and this document's page if the passed
8383    * String doesn't specify a page.
8384    */
 
8385  39178 toggle public DocumentReference resolveClassReference(String documentName)
8386    {
8387  39173 DocumentReference defaultReference = new DocumentReference(getDocumentReference().getWikiReference().getName(),
8388    XWiki.SYSTEM_SPACE, getDocumentReference().getName());
8389  39178 return getExplicitDocumentReferenceResolver().resolve(documentName, defaultReference);
8390    }
8391   
8392    /**
8393    * Transforms a XClass reference relative to this document into an absolute reference.
8394    */
 
8395  3045 toggle private DocumentReference resolveClassReference(EntityReference reference)
8396    {
8397  3045 if (reference instanceof DocumentReference) {
8398  193 return (DocumentReference) reference;
8399  2852 } else if (reference instanceof LocalDocumentReference) {
8400  284 return new DocumentReference((LocalDocumentReference) reference, getDocumentReference().getWikiReference());
8401    } else {
8402  2568 DocumentReference defaultReference =
8403    new DocumentReference(getDocumentReference().getWikiReference().getName(), XWiki.SYSTEM_SPACE,
8404    getDocumentReference().getName());
8405  2568 return getExplicitReferenceDocumentReferenceResolver().resolve(reference, defaultReference);
8406    }
8407    }
8408   
8409    /**
8410    * Return the reference of the parent document as it stored and passed to
8411    * {@link #setParentReference(EntityReference)}.
8412    * <p>
8413    * You should use {@link #getParentReference()} reference if you want the complete parent reference.
8414    *
8415    * @return the relative parent reference
8416    * @since 2.2.3
8417    */
 
8418  115369 toggle public EntityReference getRelativeParentReference()
8419    {
8420  115367 return this.parentReference;
8421    }
8422   
 
8423  48 toggle private BaseObject prepareXObject(EntityReference classReference)
8424    {
8425  48 DocumentReference absoluteClassReference = resolveClassReference(classReference);
8426  48 BaseObject bobject = getXObject(absoluteClassReference);
8427  48 if (bobject == null) {
8428  0 bobject = new BaseObject();
8429  0 bobject.setXClassReference(classReference);
8430   
8431  0 addXObject(bobject);
8432    }
8433  48 bobject.setDocumentReference(getDocumentReference());
8434  48 setMetaDataDirty(true);
8435  48 return bobject;
8436    }
8437   
8438    /**
8439    * Apply a 3 ways merge on the current document based on provided previous and new version of the document.
8440    * <p>
8441    * All 3 documents are supposed to have the same document reference and language already since that's what makes
8442    * them uniques.
8443    *
8444    * @param previousDocument the previous version of the document
8445    * @param newDocument the next version of the document
8446    * @param configuration the configuration of the merge indicates how to deal with some conflicts use cases, etc.
8447    * @param context the XWiki context
8448    * @return a repport of what happen during the merge (errors, etc.)
8449    * @since 3.2M1
8450    */
 
8451  54 toggle public MergeResult merge(XWikiDocument previousDocument, XWikiDocument newDocument,
8452    MergeConfiguration configuration, XWikiContext context)
8453    {
8454  54 MergeResult mergeResult = new MergeResult();
8455   
8456    // Title
8457  54 setTitle(MergeUtils.mergeOject(previousDocument.getTitle(), newDocument.getTitle(), getTitle(), mergeResult));
8458   
8459    // Content
8460  54 setContent(
8461    MergeUtils.mergeLines(previousDocument.getContent(), newDocument.getContent(), getContent(), mergeResult));
8462   
8463    // Syntax
8464  54 setSyntax(
8465    MergeUtils.mergeOject(previousDocument.getSyntax(), newDocument.getSyntax(), getSyntax(), mergeResult));
8466   
8467    // Default locale
8468  54 setDefaultLocale(MergeUtils.mergeOject(previousDocument.getDefaultLocale(), newDocument.getDefaultLocale(),
8469    getDefaultLocale(), mergeResult));
8470   
8471    // Parent
8472  54 setParentReference(MergeUtils.mergeOject(previousDocument.getRelativeParentReference(),
8473    newDocument.getRelativeParentReference(), getRelativeParentReference(), mergeResult));
8474   
8475    // DefaultTemplate
8476  54 setDefaultTemplate(MergeUtils.mergeOject(previousDocument.getDefaultTemplate(),
8477    newDocument.getDefaultTemplate(), getDefaultTemplate(), mergeResult));
8478   
8479    // Hidden
8480  54 setHidden(MergeUtils.mergeOject(previousDocument.isHidden(), newDocument.isHidden(), isHidden(), mergeResult));
8481   
8482    // CustomClass
8483  54 setCustomClass(MergeUtils.mergeLines(previousDocument.getCustomClass(), newDocument.getCustomClass(),
8484    getCustomClass(), mergeResult));
8485   
8486    // ValidationScript
8487  54 setValidationScript(MergeUtils.mergeLines(previousDocument.getValidationScript(),
8488    newDocument.getValidationScript(), getValidationScript(), mergeResult));
8489   
8490    // Objects
8491  54 List<List<ObjectDiff>> objectsDiff = previousDocument.getObjectDiff(previousDocument, newDocument, context);
8492  54 if (!objectsDiff.isEmpty()) {
8493    // Apply diff on result
8494  7 for (List<ObjectDiff> objectClassDiff : objectsDiff) {
8495  7 for (ObjectDiff diff : objectClassDiff) {
8496  23 BaseObject objectResult = getXObject(diff.getXClassReference(), diff.getNumber());
8497  23 BaseObject previousObject =
8498    previousDocument.getXObject(diff.getXClassReference(), diff.getNumber());
8499  23 BaseObject newObject = newDocument.getXObject(diff.getXClassReference(), diff.getNumber());
8500  23 PropertyInterface propertyResult =
8501  23 objectResult != null ? objectResult.getField(diff.getPropName()) : null;
8502  23 PropertyInterface previousProperty =
8503  23 previousObject != null ? previousObject.getField(diff.getPropName()) : null;
8504  23 PropertyInterface newProperty = newObject != null ? newObject.getField(diff.getPropName()) : null;
8505   
8506  23 if (diff.getAction() == ObjectDiff.ACTION_OBJECTADDED) {
8507  1 if (objectResult == null) {
8508  1 setXObject(newObject.getNumber(),
8509  1 configuration.isProvidedVersionsModifiables() ? newObject : newObject.clone());
8510  1 mergeResult.setModified(true);
8511    } else {
8512    // collision between DB and new: object to add but already exists in the DB
8513  0 mergeResult.getLog().error("Collision found on object [{}]", objectResult.getReference());
8514    }
8515  22 } else if (diff.getAction() == ObjectDiff.ACTION_OBJECTREMOVED) {
8516  1 if (objectResult != null) {
8517  1 if (objectResult.equals(previousObject)) {
8518  1 removeXObject(objectResult);
8519  1 mergeResult.setModified(true);
8520    } else {
8521    // collision between DB and new: object to remove but not the same as previous
8522    // version
8523  0 mergeResult.getLog().error("Collision found on object [{}]",
8524    objectResult.getReference());
8525    }
8526    } else {
8527    // Already removed from DB, lets assume the user is prescient
8528  0 mergeResult.getLog().warn("Object [{}] already removed", previousObject.getReference());
8529    }
8530  21 } else if (previousObject != null && newObject != null) {
8531  9 if (objectResult != null) {
8532  7 if (diff.getAction() == ObjectDiff.ACTION_PROPERTYADDED) {
8533  0 if (propertyResult == null) {
8534  0 objectResult.safeput(diff.getPropName(), newProperty);
8535  0 mergeResult.setModified(true);
8536    } else {
8537    // collision between DB and new: property to add but already exists in the DB
8538  0 mergeResult.getLog().error("Collision found on object property [{}]",
8539    propertyResult.getReference());
8540    }
8541  7 } else if (diff.getAction() == ObjectDiff.ACTION_PROPERTYREMOVED) {
8542  0 if (propertyResult != null) {
8543  0 if (propertyResult.equals(previousProperty)) {
8544  0 objectResult.removeField(diff.getPropName());
8545  0 mergeResult.setModified(true);
8546    } else {
8547    // collision between DB and new: supposed to be removed but the DB version is
8548    // not the same as the previous version
8549  0 mergeResult.getLog().error("Collision found on object property [{}]",
8550    propertyResult.getReference());
8551    }
8552    } else {
8553    // Already removed from DB, lets assume the user is prescient
8554  0 mergeResult.getLog().warn("Object property [{}] already removed",
8555    previousProperty.getReference());
8556    }
8557  7 } else if (diff.getAction() == ObjectDiff.ACTION_PROPERTYCHANGED) {
8558  7 if (propertyResult != null) {
8559  7 if (propertyResult.equals(previousProperty)) {
8560  6 objectResult.safeput(diff.getPropName(), newProperty);
8561  6 mergeResult.setModified(true);
8562    } else {
8563    // Try to apply a 3 ways merge on the property
8564  1 propertyResult.merge(previousProperty, newProperty, configuration, context,
8565    mergeResult);
8566    }
8567    } else {
8568    // collision between DB and new: property to modify but does not exists in DB
8569    // Lets assume it's a mistake to fix
8570  0 mergeResult.getLog().warn("Object [{}] does not exists",
8571    newProperty.getReference());
8572   
8573  0 objectResult.safeput(diff.getPropName(), newProperty);
8574  0 mergeResult.setModified(true);
8575    }
8576    }
8577    } else {
8578    // Object explitely removed from the DB, lets assume we don't care about the changes
8579  2 mergeResult.getLog().warn("Object [{}] already removed", previousObject.getReference());
8580    }
8581    }
8582    }
8583    }
8584    }
8585   
8586    // Class
8587  54 BaseClass classResult = getXClass();
8588  54 BaseClass previousClass = previousDocument.getXClass();
8589  54 BaseClass newClass = newDocument.getXClass();
8590  54 classResult.merge(previousClass, newClass, configuration, context, mergeResult);
8591   
8592    // Attachments
8593  54 List<AttachmentDiff> attachmentsDiff =
8594    previousDocument.getAttachmentDiff(previousDocument, newDocument, context);
8595  54 if (!attachmentsDiff.isEmpty()) {
8596    // Apply deleted attachment diff on result (new attachment has already been saved)
8597  8 for (AttachmentDiff diff : attachmentsDiff) {
8598  8 XWikiAttachment previousAttachment = diff.getOrigAttachment();
8599  8 XWikiAttachment nextAttachment = diff.getNewAttachment();
8600  8 XWikiAttachment attachment = getAttachment(diff.getFileName());
8601   
8602  8 switch (diff.getType()) {
8603  2 case DELETE:
8604  2 if (attachment != null) {
8605  2 try {
8606  2 if (attachment.equalsData(previousAttachment, context)) {
8607  2 removeAttachment(attachment);
8608  2 mergeResult.setModified(true);
8609    } else {
8610    // collision between DB and new: attachment modified by user
8611  0 mergeResult.getLog().error("Collision found on attachment [{}]",
8612    attachment.getReference());
8613    }
8614    } catch (XWikiException e) {
8615  0 mergeResult.getLog().error("Failed to compare attachments with reference [{}]",
8616    attachment.getReference());
8617    }
8618    } else {
8619    // Already removed from DB, lets assume the user is prescient
8620  0 mergeResult.getLog().warn("Attachment [{}] already removed",
8621    previousAttachment.getReference());
8622    }
8623  2 break;
8624  5 case INSERT:
8625  5 if (attachment != null) {
8626  0 try {
8627  0 if (!attachment.equalsData(nextAttachment, context)) {
8628    // collision between DB and new: attachment to add but a different one already
8629    // exists in the DB
8630  0 mergeResult.getLog().error("Collision found on attachment [{}]",
8631    attachment.getReference());
8632    } else {
8633    // Already added to the DB, lets assume the user is prescient
8634  0 mergeResult.getLog().warn("Attachment [{}] already added",
8635    previousAttachment.getReference());
8636    }
8637    } catch (XWikiException e) {
8638  0 mergeResult.getLog().error("Failed to compare attachments with reference [{}]",
8639    attachment.getReference());
8640    }
8641    } else {
8642  5 addAttachment(configuration.isProvidedVersionsModifiables() ? nextAttachment
8643    : (XWikiAttachment) nextAttachment.clone());
8644  5 mergeResult.setModified(true);
8645    }
8646  5 break;
8647  1 case CHANGE:
8648  1 if (attachment != null) {
8649  1 attachment.merge(previousAttachment, nextAttachment, configuration, context, mergeResult);
8650    } else {
8651    // collision between DB and new: attachment modified but does not exist in the DB
8652  0 mergeResult.getLog().error("Collision found on attachment [{}]",
8653    previousAttachment.getReference());
8654    }
8655  1 break;
8656  0 default:
8657  0 break;
8658    }
8659    }
8660    }
8661   
8662  54 return mergeResult;
8663    }
8664   
8665    /**
8666    * Apply modification comming from provided document.
8667    * <p>
8668    * Thid method does not take into account versions and author related informations and the provided document should
8669    * have the same reference. Like {@link #merge(XWikiDocument, XWikiDocument, MergeConfiguration, XWikiContext)},
8670    * this method is dealing with "real" data and should not change anything related to version management and document
8671    * identifier.
8672    * <p>
8673    * Important note: this method does not take care of attachments contents related operations and only remove
8674    * attachments which need to be removed from the list. For memory handling reasons all attachments contents related
8675    * operations should be done elsewhere.
8676    *
8677    * @param document the document to apply
8678    * @return false is nothing changed
8679    */
 
8680  4 toggle public boolean apply(XWikiDocument document)
8681    {
8682  4 return apply(document, true);
8683    }
8684   
8685    /**
8686    * Apply modification comming from provided document.
8687    * <p>
8688    * Thid method does not take into account versions and author related informations and the provided document should
8689    * have the same reference. Like {@link #merge(XWikiDocument, XWikiDocument, MergeConfiguration, XWikiContext)},
8690    * this method is dealing with "real" data and should not change everything related to version management and
8691    * document identifier.
8692    *
8693    * @param document the document to apply
8694    * @return false is nothing changed
8695    */
 
8696  6 toggle public boolean apply(XWikiDocument document, boolean clean)
8697    {
8698  6 boolean modified = false;
8699   
8700    // /////////////////////////////////
8701    // Document
8702   
8703  6 if (!StringUtils.equals(getContent(), document.getContent())) {
8704  4 setContent(document.getContent());
8705  4 modified = true;
8706    }
8707  6 if (ObjectUtils.notEqual(getSyntax(), document.getSyntax())) {
8708  2 setSyntax(document.getSyntax());
8709  2 modified = true;
8710    }
8711   
8712  6 if (ObjectUtils.notEqual(getDefaultLocale(), document.getDefaultLocale())) {
8713  2 setDefaultLocale(document.getDefaultLocale());
8714  2 modified = true;
8715    }
8716   
8717  6 if (!StringUtils.equals(getTitle(), document.getTitle())) {
8718  1 setTitle(document.getTitle());
8719  1 modified = true;
8720    }
8721   
8722  6 if (!StringUtils.equals(getDefaultTemplate(), document.getDefaultTemplate())) {
8723  0 setDefaultTemplate(document.getDefaultTemplate());
8724  0 modified = true;
8725    }
8726  6 if (ObjectUtils.notEqual(getRelativeParentReference(), document.getRelativeParentReference())) {
8727  1 setParentReference(document.getRelativeParentReference());
8728  1 modified = true;
8729    }
8730   
8731  6 if (!StringUtils.equals(getCustomClass(), document.getCustomClass())) {
8732  0 setCustomClass(document.getCustomClass());
8733  0 modified = true;
8734    }
8735   
8736  6 if (!StringUtils.equals(getValidationScript(), document.getValidationScript())) {
8737  0 setValidationScript(document.getValidationScript());
8738  0 modified = true;
8739    }
8740   
8741  6 if (isHidden() != document.isHidden()) {
8742  0 setHidden(document.isHidden());
8743  0 modified = true;
8744    }
8745   
8746  6 if (!StringUtils.equals(getValidationScript(), document.getValidationScript())) {
8747  0 setValidationScript(document.getValidationScript());
8748  0 modified = true;
8749    }
8750   
8751    // /////////////////////////////////
8752    // XObjects
8753   
8754  6 if (clean) {
8755    // Delete objects that don't exist anymore
8756  6 for (List<BaseObject> objects : getXObjects().values()) {
8757    // Duplicate the list since we are potentially going to modify it
8758  7 for (BaseObject originalObj : new ArrayList<BaseObject>(objects)) {
8759  7 if (originalObj != null) {
8760  7 BaseObject newObj =
8761    document.getXObject(originalObj.getXClassReference(), originalObj.getNumber());
8762  7 if (newObj == null) {
8763    // The object was deleted
8764  2 removeXObject(originalObj);
8765  2 modified = true;
8766    }
8767    }
8768    }
8769    }
8770    }
8771    // Add new objects or update existing objects
8772  6 for (List<BaseObject> objects : document.getXObjects().values()) {
8773  9 for (BaseObject newObj : objects) {
8774  9 if (newObj != null) {
8775  9 BaseObject originalObj = getXObject(newObj.getXClassReference(), newObj.getNumber());
8776  9 if (originalObj == null) {
8777    // The object added or modified
8778  4 setXObject(newObj.getNumber(), newObj);
8779  4 modified = true;
8780    } else {
8781    // The object added or modified
8782  5 modified |= originalObj.apply(newObj, clean);
8783    }
8784    }
8785    }
8786    }
8787   
8788    // /////////////////////////////////
8789    // XClass
8790   
8791  6 modified |= getXClass().apply(document.getXClass(), clean);
8792  6 if (ObjectUtils.notEqual(getXClassXML(), document.getXClassXML())) {
8793  1 setXClassXML(document.getXClassXML());
8794  1 modified = true;
8795    }
8796   
8797    // /////////////////////////////////
8798    // Attachments
8799   
8800  6 if (clean) {
8801    // Delete attachments that don't exist anymore
8802  6 for (XWikiAttachment attachment : new ArrayList<XWikiAttachment>(getAttachmentList())) {
8803  0 if (document.getAttachment(attachment.getFilename()) == null) {
8804  0 removeAttachment(attachment);
8805    }
8806    }
8807    }
8808    // Add new attachments or update existing attachments
8809  6 for (XWikiAttachment attachment : document.getAttachmentList()) {
8810  0 XWikiAttachment originalAttachment = getAttachment(attachment.getFilename());
8811  0 if (originalAttachment == null) {
8812  0 addAttachment(attachment);
8813    } else {
8814  0 originalAttachment.apply(attachment);
8815    }
8816    }
8817   
8818  6 return modified;
8819    }
8820    }