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

File BaseElement.java

 

Coverage histogram

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

Code metrics

48
105
29
1
482
280
61
0.58
3.62
29
2.1

Classes

Class Line # Actions
BaseElement 56 105 0% 61 44
0.758241875.8%
 

Contributing tests

This file is covered by 214 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.objects;
21   
22    import java.io.IOException;
23    import java.io.Serializable;
24    import java.io.StringWriter;
25   
26    import javax.inject.Provider;
27   
28    import org.apache.commons.lang3.StringUtils;
29    import org.dom4j.Element;
30    import org.dom4j.io.DocumentResult;
31    import org.slf4j.Logger;
32    import org.slf4j.LoggerFactory;
33    import org.xwiki.filter.input.StringInputSource;
34    import org.xwiki.filter.xar.output.XAROutputProperties;
35    import org.xwiki.filter.xml.output.DefaultResultOutputTarget;
36    import org.xwiki.localization.ContextualLocalizationManager;
37    import org.xwiki.model.reference.DocumentReference;
38    import org.xwiki.model.reference.EntityReference;
39    import org.xwiki.model.reference.EntityReferenceSerializer;
40   
41    import com.xpn.xwiki.XWikiContext;
42    import com.xpn.xwiki.XWikiException;
43    import com.xpn.xwiki.doc.XWikiDocument;
44    import com.xpn.xwiki.doc.merge.MergeConfiguration;
45    import com.xpn.xwiki.doc.merge.MergeResult;
46    import com.xpn.xwiki.internal.filter.XWikiDocumentFilterUtils;
47    import com.xpn.xwiki.internal.merge.MergeUtils;
48    import com.xpn.xwiki.util.Util;
49    import com.xpn.xwiki.web.Utils;
50   
51    /**
52    * Base class for representing an element having a name (either a reference of a free form name) and a pretty name.
53    *
54    * @version $Id: c77bd51f37733c4611d22198473a4558768cb30b $
55    */
 
56    public abstract class BaseElement<R extends EntityReference> implements ElementInterface, Serializable
57    {
58    protected static final Logger LOGGER = LoggerFactory.getLogger(BaseObject.class);
59   
60    /**
61    * Full reference of this element.
62    *
63    * @since 3.2M1
64    */
65    protected R referenceCache;
66   
67    /**
68    * Reference to the document in which this element is defined (for elements where this make sense, for example for
69    * an XClass or a XObject).
70    *
71    * @since 5.3M1
72    */
73    protected DocumentReference documentReference;
74   
75    /**
76    * The owner document, if this element was obtained from a document.
77    *
78    * @since 5.3M1
79    */
80    protected transient XWikiDocument ownerDocument;
81   
82    /**
83    * Free form name (for elements which don't point to a reference, for example for instances of {@link BaseProperty}
84    * ).
85    */
86    private String name;
87   
88    private String prettyName;
89   
90    /**
91    * Used to convert a proper Document Reference to a string but without the wiki name.
92    */
93    private EntityReferenceSerializer<String> localEntityReferenceSerializer;
94   
95    /**
96    * Used to build uid string for the getId() hash.
97    */
98    private EntityReferenceSerializer<String> localUidStringEntityReferenceSerializer;
99   
100    private ContextualLocalizationManager localization;
101   
 
102  279034 toggle @Override
103    public R getReference()
104    {
105  279032 if (this.referenceCache == null) {
106  51245 this.referenceCache = createReference();
107    }
108   
109  279028 return this.referenceCache;
110    }
111   
112    /**
113    * @since 3.2M1
114    */
 
115  0 toggle protected R createReference()
116    {
117  0 return null;
118    }
119   
 
120  336372 toggle @Override
121    public DocumentReference getDocumentReference()
122    {
123    // Object using name without setting a reference are not allowed to retrieve the reference
124  336368 if (this.documentReference == null && this.name != null) {
125  0 throw new IllegalStateException(
126    "BaseElement#getDocumentReference could not be called when a non-reference Name has been set.");
127    }
128   
129  336374 return this.documentReference;
130    }
131   
132    /**
133    * {@inheritDoc}
134    * <p>
135    * Note that this method is used by Hibernate for saving an element.
136    * </p>
137    *
138    * @see com.xpn.xwiki.objects.ElementInterface#getName()
139    */
 
140  4533681 toggle @Override
141    public String getName()
142    {
143    // If the name is null then serialize the reference as a string.
144  4533683 if (this.name == null && this.documentReference != null) {
145  30817 this.name = getLocalEntityReferenceSerializer().serialize(this.documentReference);
146    }
147   
148  4533691 return this.name;
149    }
150   
 
151  263594 toggle @Override
152    public void setDocumentReference(DocumentReference reference)
153    {
154    // If the name is already set then reset it since we're now using a reference
155  263593 this.documentReference = reference;
156  263585 this.name = null;
157  263586 this.referenceCache = null;
158    }
159   
160    /**
161    * {@inheritDoc}
162    * <p>
163    * Note that this method is used by Hibernate for loading an element.
164    * </p>
165    *
166    * @see com.xpn.xwiki.objects.ElementInterface#setName(java.lang.String)
167    */
 
168  2821842 toggle @Override
169    public void setName(String name)
170    {
171    // If a reference is already set, then you cannot set a name
172  2821879 if (this.documentReference != null) {
173  0 throw new IllegalStateException("BaseElement#setName could not be called when a reference has been set.");
174    }
175   
176  2821603 this.name = name;
177  2821608 this.referenceCache = null;
178    }
179   
 
180  961576 toggle public String getPrettyName()
181    {
182  961580 return this.prettyName;
183    }
184   
 
185  844356 toggle public void setPrettyName(String name)
186    {
187  844350 this.prettyName = name;
188    }
189   
190    /**
191    * @return the component used to build uid string for the getId() hash
192    * @since 4.0M1
193    */
 
194  293765 toggle protected EntityReferenceSerializer<String> getLocalUidStringEntityReferenceSerializer()
195    {
196  293765 if (this.localUidStringEntityReferenceSerializer == null) {
197  13835 this.localUidStringEntityReferenceSerializer =
198    Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, "local/uid");
199    }
200   
201  293765 return this.localUidStringEntityReferenceSerializer;
202    }
203   
204    /**
205    * @return the component used to convert a proper Document Reference to a string but without the wiki name.
206    * @since 6.3M1
207    */
 
208  48076 toggle protected EntityReferenceSerializer<String> getLocalEntityReferenceSerializer()
209    {
210  48076 if (this.localEntityReferenceSerializer == null) {
211  22215 this.localEntityReferenceSerializer = Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, "local");
212    }
213   
214  48077 return this.localEntityReferenceSerializer;
215    }
216   
 
217  4993 toggle protected ContextualLocalizationManager getLocalization()
218    {
219  4993 if (this.localization == null) {
220  375 this.localization = Utils.getComponent(ContextualLocalizationManager.class);
221    }
222   
223  4993 return this.localization;
224    }
225   
 
226  4993 toggle protected String localizePlain(String key, Object... parameters)
227    {
228  4993 return getLocalization().getTranslationPlain(key, parameters);
229    }
230   
 
231  0 toggle protected String localizePlainOrKey(String key, Object... parameters)
232    {
233  0 return StringUtils.defaultString(getLocalization().getTranslationPlain(key, parameters), key);
234    }
235   
236    /**
237    * @return a unique identifier representing this element reference to be used for {@code hashCode()}.
238    * @since 4.0M1
239    */
 
240  293764 toggle protected String getLocalKey()
241    {
242    // The R40000XWIKI6990DataMigration use the same algorithm to compute object id. It should be properly synced.
243  293764 return getLocalUidStringEntityReferenceSerializer().serialize(getReference());
244    }
245   
246    /**
247    * Return an truncated MD5 hash of the local key computed in {@link #getLocalKey()}.
248    *
249    * @return the identifier used by hibernate for storage.
250    * @since 4.0M1
251    */
 
252  293765 toggle public long getId()
253    {
254  293765 String key = getLocalKey();
255   
256  293765 if (key != null) {
257    // The R40000XWIKI6990DataMigration use the same algorithm to compute object id. It should be properly
258    // synced.
259  293763 return Util.getHash(key);
260    }
261   
262  2 return 0;
263    }
264   
265    /**
266    * Dummy function, do hibernate is always happy.
267    *
268    * @param id the identifier assigned by hibernate.
269    * @since 4.0M1
270    */
 
271  13583 toggle public void setId(long id)
272    {
273    }
274   
 
275  0 toggle @Override
276    public int hashCode()
277    {
278  0 return (int) Util.getHash(getLocalKey());
279    }
280   
 
281  66729 toggle @Override
282    public boolean equals(Object el)
283    {
284    // Same Java object, they sure are equal
285  66729 if (this == el) {
286  0 return true;
287    }
288   
289  66729 if (el == null || !(el.getClass().equals(this.getClass()))) {
290  2 return false;
291    }
292   
293  66727 BaseElement element = (BaseElement) el;
294   
295  66727 if (element.documentReference != null) {
296  128 if (!element.documentReference.equals(this.documentReference)) {
297  20 return false;
298    }
299    } else {
300  66599 if (this.documentReference != null) {
301  0 return false;
302    }
303  66599 if (element.name == null) {
304  7683 if (this.name != null) {
305  0 return false;
306    }
307  58916 } else if (!element.name.equals(this.name)) {
308  0 return false;
309    }
310    }
311   
312  66707 if (element.getPrettyName() == null) {
313  59024 if (getPrettyName() != null) {
314  0 return false;
315    }
316  7683 } else if (!element.getPrettyName().equals(getPrettyName())) {
317  3 return false;
318    }
319   
320  66704 return true;
321    }
322   
 
323  925261 toggle @Override
324    public BaseElement clone()
325    {
326  925268 BaseElement element;
327  925271 try {
328  925272 element = (BaseElement) super.clone();
329   
330  925281 element.setOwnerDocument(getOwnerDocument());
331   
332    // Make sure we clone either the reference or the name depending on which one is used.
333  925286 if (this.documentReference != null) {
334  52062 element.setDocumentReference(getDocumentReference());
335  873235 } else if (this.name != null) {
336  791155 element.setName(getName());
337    }
338   
339  925279 element.setPrettyName(getPrettyName());
340    } catch (Exception e) {
341    // This should not happen
342  0 element = null;
343    }
344   
345  925267 return element;
346    }
347   
 
348  1 toggle @Override
349    public void merge(ElementInterface previousElement, ElementInterface newElement, MergeConfiguration configuration,
350    XWikiContext context, MergeResult mergeResult)
351    {
352  1 setPrettyName(MergeUtils.mergeOject(((BaseElement) previousElement).getPrettyName(),
353    ((BaseElement) newElement).getPrettyName(), getPrettyName(), mergeResult));
354    }
355   
 
356  34 toggle @Override
357    public boolean apply(ElementInterface newElement, boolean clean)
358    {
359  34 boolean modified = false;
360   
361  34 BaseElement<R> newBaseElement = (BaseElement<R>) newElement;
362   
363    // Pretty name
364  34 if (!StringUtils.equals(newBaseElement.getPrettyName(), getPrettyName())) {
365  0 setPrettyName(newBaseElement.getPrettyName());
366  0 modified = true;
367    }
368   
369  34 return modified;
370    }
371   
372    /**
373    * Set the owner document of this element.
374    *
375    * @param ownerDocument The owner document.
376    * @since 5.3M1
377    */
 
378  6139575 toggle public void setOwnerDocument(XWikiDocument ownerDocument)
379    {
380  6138856 this.ownerDocument = ownerDocument;
381    }
382   
383    /**
384    * @return the owner document of this element.
385    * @since 5.3M1
386    */
 
387  2026682 toggle public XWikiDocument getOwnerDocument()
388    {
389  2026692 return this.ownerDocument;
390    }
391   
392    /**
393    * Get XWiki context from execution context.
394    *
395    * @return the XWiki context for the current thread
396    * @since 9.0RC1
397    */
 
398  0 toggle protected XWikiContext getXWikiContext()
399    {
400  0 Provider<XWikiContext> xcontextProvider = Utils.getComponent(XWikiContext.TYPE_PROVIDER);
401   
402  0 if (xcontextProvider != null) {
403  0 return xcontextProvider.get();
404    }
405   
406  0 return null;
407    }
408   
 
409  2 toggle protected void fromXML(Element oel) throws XWikiException
410    {
411    // Serialize the Document (could not find a way to convert a dom4j Element into a usable StAX source)
412  2 StringWriter writer = new StringWriter();
413  2 try {
414  2 org.dom4j.io.XMLWriter domWriter = new org.dom4j.io.XMLWriter(writer);
415  2 domWriter.write(oel);
416  2 domWriter.flush();
417    } catch (IOException e) {
418  0 throw new XWikiException(XWikiException.MODULE_XWIKI_DOC, XWikiException.ERROR_DOC_XML_PARSING,
419    "Error parsing xml", e, null);
420    }
421   
422    // Actually parse the XML
423  2 fromXML(writer.toString());
424    }
425   
426    /**
427    * @param source the XML to read
428    * @throws XWikiException when failing to parse XML
429    */
 
430  3615 toggle public void fromXML(String source) throws XWikiException
431    {
432  3614 if (!source.isEmpty()) {
433  1743 try {
434  1743 Utils.getComponent(XWikiDocumentFilterUtils.class).importEntity(this, new StringInputSource(source));
435    } catch (Exception e) {
436  0 throw new XWikiException(XWikiException.MODULE_XWIKI_DOC, XWikiException.ERROR_DOC_XML_PARSING,
437    "Error parsing xml", e, null);
438    }
439    }
440    }
441   
 
442  0 toggle protected Element toXML()
443    {
444  0 DocumentResult domResult = new DocumentResult();
445   
446  0 try {
447  0 Utils.getComponent(XWikiDocumentFilterUtils.class).exportEntity(this,
448    new DefaultResultOutputTarget(domResult));
449    } catch (Exception e) {
450  0 LOGGER.error("Failed to serialize element to XML", e);
451   
452  0 return null;
453    }
454   
455  0 return domResult.getDocument().getRootElement();
456    }
457   
458    /**
459    * @param format true if the XML should be formated
460    * @return the XML as a String
461    * @since 9.0RC1
462    */
 
463  1260 toggle public String toXMLString(boolean format)
464    {
465  1260 XAROutputProperties xarProperties = new XAROutputProperties();
466  1260 xarProperties.setFormat(false);
467   
468  1260 try {
469  1260 return Utils.getComponent(XWikiDocumentFilterUtils.class).exportEntity(this, xarProperties);
470    } catch (Exception e) {
471  0 LOGGER.error("Failed to serialize collection to XML", e);
472   
473  0 return "";
474    }
475    }
476   
 
477  0 toggle @Override
478    public String toString()
479    {
480  0 return toXMLString(true);
481    }
482    }