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

File PropertyClass.java

 

Coverage histogram

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

Code metrics

64
201
68
1
783
526
107
0.53
2.96
68
1.57

Classes

Class Line # Actions
PropertyClass 62 201 0% 107 141
0.576576657.7%
 

Contributing tests

This file is covered by 95 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.classes;
21   
22    import java.util.Iterator;
23    import java.util.List;
24   
25    import javax.script.ScriptContext;
26   
27    import org.apache.commons.lang3.StringUtils;
28    import org.apache.ecs.xhtml.input;
29    import org.dom4j.Element;
30    import org.dom4j.dom.DOMElement;
31    import org.hibernate.mapping.Property;
32    import org.slf4j.Logger;
33    import org.slf4j.LoggerFactory;
34    import org.xwiki.model.reference.ClassPropertyReference;
35    import org.xwiki.model.reference.DocumentReference;
36    import org.xwiki.model.reference.EntityReference;
37    import org.xwiki.script.ScriptContextManager;
38    import org.xwiki.security.authorization.AuthorExecutor;
39    import org.xwiki.template.Template;
40    import org.xwiki.template.TemplateManager;
41   
42    import com.xpn.xwiki.XWikiContext;
43    import com.xpn.xwiki.XWikiException;
44    import com.xpn.xwiki.doc.XWikiDocument;
45    import com.xpn.xwiki.doc.merge.MergeConfiguration;
46    import com.xpn.xwiki.doc.merge.MergeResult;
47    import com.xpn.xwiki.internal.xml.XMLAttributeValueFilter;
48    import com.xpn.xwiki.objects.BaseCollection;
49    import com.xpn.xwiki.objects.BaseObject;
50    import com.xpn.xwiki.objects.BaseProperty;
51    import com.xpn.xwiki.objects.meta.MetaClass;
52    import com.xpn.xwiki.objects.meta.PropertyMetaClass;
53    import com.xpn.xwiki.validation.XWikiValidationStatus;
54    import com.xpn.xwiki.web.Utils;
55   
56    /**
57    * Represents an XClass property and contains property definitions (eg "relational storage", "display type",
58    * "separator", "multi select", etc). Each property definition is of type {@link BaseProperty}.
59    *
60    * @version $Id: 72deaa8bc739a1d60b978703502a14ac55adcd0f $
61    */
 
62    public class PropertyClass extends BaseCollection<ClassPropertyReference>
63    implements PropertyClassInterface, Comparable<PropertyClass>
64    {
65    /**
66    * Logging helper object.
67    */
68    private static final Logger LOGGER = LoggerFactory.getLogger(PropertyClass.class);
69   
70    /**
71    * Identifier used to specify that the property has a custom displayer in the XClass itself.
72    */
73    private static final String CLASS_DISPLAYER_IDENTIFIER = "class";
74   
75    /**
76    * Identifier prefix used to specify that the property has a custom displayer in a wiki document.
77    */
78    private static final String DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX = "doc:";
79   
80    /**
81    * Identifier prefix used to specify that the property has a custom displayer in a velocity template.
82    */
83    private static final String TEMPLATE_DISPLAYER_IDENTIFIER_PREFIX = "template:";
84   
85    private BaseClass xclass;
86   
87    private long id;
88   
89    private PropertyMetaClass pMetaClass;
90   
91    protected String cachedCustomDisplayer;
92   
 
93  2 toggle public PropertyClass()
94    {
95    }
96   
 
97  61038 toggle public PropertyClass(String name, String prettyname, PropertyMetaClass xWikiClass)
98    {
99  61038 setName(name);
100  61039 setPrettyName(prettyname);
101  61040 setxWikiClass(xWikiClass);
102  61040 setUnmodifiable(false);
103  61039 setDisabled(false);
104    }
105   
 
106  8635 toggle @Override
107    protected ClassPropertyReference createReference()
108    {
109  8635 return new ClassPropertyReference(getName(), this.xclass.getReference());
110    }
111   
 
112  10307 toggle @Override
113    public BaseClass getXClass(XWikiContext context)
114    {
115  10307 return getxWikiClass();
116    }
117   
 
118  10307 toggle public BaseClass getxWikiClass()
119    {
120  10307 if (this.pMetaClass == null) {
121  422 MetaClass metaClass = MetaClass.getMetaClass();
122  422 this.pMetaClass = (PropertyMetaClass) metaClass.get(getClassType());
123    }
124  10307 return this.pMetaClass;
125    }
126   
 
127  64021 toggle public void setxWikiClass(BaseClass xWikiClass)
128    {
129  64021 this.pMetaClass = (PropertyMetaClass) xWikiClass;
130    }
131   
 
132  322464 toggle @Override
133    public BaseCollection getObject()
134    {
135  322464 return this.xclass;
136    }
137   
 
138  209498 toggle @Override
139    public void setObject(BaseCollection object)
140    {
141  209499 this.xclass = (BaseClass) object;
142    }
143   
 
144  2837 toggle public String getFieldFullName()
145    {
146  2837 if (getObject() == null) {
147  432 return getName();
148    }
149  2405 return getObject().getName() + "_" + getName();
150    }
151   
 
152  117552 toggle @Override
153    public long getId()
154    {
155  117552 if (getObject() == null) {
156  0 return this.id;
157    }
158  117552 return getObject().getId();
159    }
160   
 
161  0 toggle @Override
162    public void setId(long id)
163    {
164  0 this.id = id;
165    }
166   
 
167  0 toggle @Override
168    public String toString(BaseProperty property)
169    {
170  0 return property.toText();
171    }
172   
 
173  0 toggle @Override
174    public BaseProperty fromString(String value)
175    {
176  0 return null;
177    }
178   
 
179  0 toggle public BaseProperty newPropertyfromXML(Element ppcel)
180    {
181  0 String value = ppcel.getText();
182  0 return fromString(value);
183    }
184   
 
185  0 toggle @Override
186    public void displayHidden(StringBuffer buffer, String name, String prefix, BaseCollection object,
187    XWikiContext context)
188    {
189  0 input input = new input();
190  0 input.setAttributeFilter(new XMLAttributeValueFilter());
191  0 BaseProperty prop = (BaseProperty) object.safeget(name);
192  0 if (prop != null) {
193  0 input.setValue(prop.toText());
194    }
195   
196  0 input.setType("hidden");
197  0 input.setName(prefix + name);
198  0 input.setID(prefix + name);
199  0 buffer.append(input.toString());
200    }
201   
 
202  1028 toggle @Override
203    public void displayView(StringBuffer buffer, String name, String prefix, BaseCollection object,
204    XWikiContext context)
205    {
206  1028 BaseProperty prop = (BaseProperty) object.safeget(name);
207  1028 if (prop != null) {
208  796 buffer.append(prop.toText());
209    }
210    }
211   
 
212  0 toggle @Override
213    public void displayEdit(StringBuffer buffer, String name, String prefix, BaseCollection object,
214    XWikiContext context)
215    {
216  0 input input = new input();
217  0 input.setAttributeFilter(new XMLAttributeValueFilter());
218   
219  0 BaseProperty prop = (BaseProperty) object.safeget(name);
220  0 if (prop != null) {
221  0 input.setValue(prop.toText());
222    }
223   
224  0 input.setType("text");
225  0 input.setName(prefix + name);
226  0 input.setID(prefix + name);
227  0 input.setDisabled(isDisabled());
228  0 buffer.append(input.toString());
229    }
230   
 
231  1 toggle public String displayHidden(String name, String prefix, BaseCollection object, XWikiContext context)
232    {
233  1 StringBuffer buffer = new StringBuffer();
234  1 displayHidden(buffer, name, prefix, object, context);
235  1 return buffer.toString();
236    }
237   
 
238  0 toggle public String displayHidden(String name, BaseCollection object, XWikiContext context)
239    {
240  0 return displayHidden(name, "", object, context);
241    }
242   
 
243  460 toggle public String displayView(String name, String prefix, BaseCollection object, XWikiContext context)
244    {
245  460 StringBuffer buffer = new StringBuffer();
246  460 displayView(buffer, name, prefix, object, context);
247  460 return buffer.toString();
248    }
249   
 
250  133 toggle public String displayView(String name, BaseCollection object, XWikiContext context)
251    {
252  133 return displayView(name, "", object, context);
253    }
254   
 
255  2039 toggle public String displayEdit(String name, String prefix, BaseCollection object, XWikiContext context)
256    {
257  2039 StringBuffer buffer = new StringBuffer();
258  2039 displayEdit(buffer, name, prefix, object, context);
259  2039 return buffer.toString();
260    }
261   
 
262  0 toggle public String displayEdit(String name, BaseCollection object, XWikiContext context)
263    {
264  0 return displayEdit(name, "", object, context);
265    }
266   
 
267  1858 toggle public boolean isCustomDisplayed(XWikiContext context)
268    {
269  1858 return (StringUtils.isNotEmpty(getCachedDefaultCustomDisplayer(context)));
270    }
271   
 
272  166 toggle public void displayCustom(StringBuffer buffer, String fieldName, String prefix, String type, BaseObject object,
273    final XWikiContext context) throws XWikiException
274    {
275  166 String content = "";
276  166 try {
277  166 ScriptContext scontext = Utils.getComponent(ScriptContextManager.class).getCurrentScriptContext();
278  166 scontext.setAttribute("name", fieldName, ScriptContext.ENGINE_SCOPE);
279  166 scontext.setAttribute("prefix", prefix, ScriptContext.ENGINE_SCOPE);
280    // The PropertyClass instance can be used to access meta properties in the custom displayer (e.g.
281    // dateFormat, multiSelect). It can be obtained from the XClass of the given object but only if the property
282    // has been added to the XClass. We need to have it in the Velocity context for the use case when an XClass
283    // property needs to be previewed before being added to the XClass.
284  166 scontext.setAttribute("field", new com.xpn.xwiki.api.PropertyClass(this, context), ScriptContext.ENGINE_SCOPE);
285  166 scontext.setAttribute("object", new com.xpn.xwiki.api.Object(object, context), ScriptContext.ENGINE_SCOPE);
286  166 scontext.setAttribute("type", type, ScriptContext.ENGINE_SCOPE);
287   
288  166 BaseProperty prop = (BaseProperty) object.safeget(fieldName);
289  166 if (prop != null) {
290  126 scontext.setAttribute("value", prop.getValue(), ScriptContext.ENGINE_SCOPE);
291    } else {
292    // The $value property can exist in the velocity context, we overwrite it to make sure we don't get a
293    // wrong value in the displayer when the property does not exist yet.
294  40 scontext.setAttribute("value", null, ScriptContext.ENGINE_SCOPE);
295    }
296   
297  166 String customDisplayer = getCachedDefaultCustomDisplayer(context);
298  166 if (StringUtils.isNotEmpty(customDisplayer)) {
299  166 if (customDisplayer.equals(CLASS_DISPLAYER_IDENTIFIER)) {
300  41 final String rawContent = getCustomDisplay();
301  41 XWikiDocument classDocument =
302    context.getWiki().getDocument(getObject().getDocumentReference(), context);
303  41 final String classSyntax = classDocument.getSyntax().toIdString();
304    // Using author reference since the document content is not relevant in this case.
305  41 DocumentReference authorReference = classDocument.getAuthorReference();
306   
307    // Make sure we render the custom displayer with the rights of the user who wrote it (i.e. class
308    // document author).
309  41 content = renderContentInContext(rawContent, classSyntax, authorReference, context);
310  125 } else if (customDisplayer.startsWith(DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX)) {
311  0 XWikiDocument displayerDoc = context.getWiki().getDocument(
312    StringUtils.substringAfter(customDisplayer, DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX), context);
313  0 final String rawContent = displayerDoc.getContent();
314  0 final String displayerDocSyntax = displayerDoc.getSyntax().toIdString();
315  0 DocumentReference authorReference = displayerDoc.getContentAuthorReference();
316   
317    // Make sure we render the custom displayer with the rights of the user who wrote it (i.e. displayer
318    // document content author).
319  0 content = renderContentInContext(rawContent, displayerDocSyntax, authorReference, context);
320  125 } else if (customDisplayer.startsWith(TEMPLATE_DISPLAYER_IDENTIFIER_PREFIX)) {
321  125 content = context.getWiki().evaluateTemplate(
322    StringUtils.substringAfter(customDisplayer, TEMPLATE_DISPLAYER_IDENTIFIER_PREFIX), context);
323    }
324    }
325    } catch (Exception e) {
326  0 throw new XWikiException(XWikiException.MODULE_XWIKI_CLASSES,
327    XWikiException.ERROR_XWIKI_CLASSES_CANNOT_PREPARE_CUSTOM_DISPLAY,
328    "Exception while preparing the custom display of " + fieldName, e, null);
329   
330    }
331  166 buffer.append(content);
332    }
333   
334    /**
335    * Render content in the current document's context with the rights of the given user.
336    *
337    * @since 8.3M2
338    */
 
339  41 toggle protected String renderContentInContext(final String content, final String syntax, DocumentReference authorReference,
340    final XWikiContext context) throws Exception
341    {
342  41 return Utils.getComponent(AuthorExecutor.class)
343    .call(() -> context.getDoc().getRenderedContent(content, syntax, context), authorReference);
344    }
345   
 
346  0 toggle @Override
347    public String getClassName()
348    {
349  0 BaseClass bclass = getxWikiClass();
350  0 return (bclass == null) ? "" : bclass.getName();
351    }
352   
353    // In property classes we need to store this info in the HashMap for fields
354    // This way it is readable by the displayEdit/displayView functions..
 
355  897959 toggle @Override
356    public String getName()
357    {
358  897964 return getStringValue("name");
359    }
360   
 
361  119410 toggle @Override
362    public void setName(String name)
363    {
364  119410 setStringValue("name", name);
365    }
366   
 
367  2067 toggle public String getCustomDisplay()
368    {
369  2067 return getStringValue("customDisplay");
370    }
371   
 
372  1 toggle public void setCustomDisplay(String value)
373    {
374  1 setLargeStringValue("customDisplay", value);
375    }
376   
 
377  110106 toggle @Override
378    public String getPrettyName()
379    {
380  110107 return getStringValue("prettyName");
381    }
382   
 
383  454 toggle public String getPrettyName(XWikiContext context)
384    {
385  454 return getTranslatedPrettyName(context);
386    }
387   
 
388  1173 toggle public String getTranslatedPrettyName(XWikiContext context)
389    {
390  1173 String msgName = getFieldFullName();
391  1173 if ((context == null) || (context.getWiki() == null)) {
392  0 return getPrettyName();
393    }
394   
395  1173 String prettyName = localizePlain(msgName);
396  1173 if (prettyName == null) {
397  392 return getPrettyName();
398    }
399  781 return prettyName;
400    }
401   
 
402  163954 toggle @Override
403    public void setPrettyName(String prettyName)
404    {
405  163955 setStringValue("prettyName", prettyName);
406    }
407   
 
408  0 toggle public String getTooltip()
409    {
410  0 return getLargeStringValue("tooltip");
411    }
412   
413    /**
414    * Gets international tooltip
415    *
416    * @param context
417    * @return
418    */
 
419  0 toggle public String getTooltip(XWikiContext context)
420    {
421  0 String tooltipName = getFieldFullName() + "_tooltip";
422  0 String tooltip = localizePlain(tooltipName);
423  0 if (tooltip == null) {
424  0 tooltipName = getLargeStringValue("tooltip");
425  0 if ((tooltipName != null) && (!tooltipName.trim().equals(""))) {
426  0 tooltip = localizePlainOrKey(tooltipName, tooltipName);
427    }
428    }
429  0 return tooltip;
430    }
431   
 
432  0 toggle public void setTooltip(String tooltip)
433    {
434  0 setLargeStringValue("tooltip", tooltip);
435    }
436   
 
437  240813 toggle @Override
438    public int getNumber()
439    {
440  240814 return getIntValue("number");
441    }
442   
 
443  102958 toggle @Override
444    public void setNumber(int number)
445    {
446  102958 setIntValue("number", number);
447    }
448   
449    /**
450    * Each type of XClass property is identified by a string that specifies the data type of the property value (e.g.
451    * 'String', 'Number', 'Date') without disclosing implementation details. The internal implementation of an XClass
452    * property type can change over time but its {@code classType} should not.
453    * <p>
454    * The {@code classType} can be used as a hint to lookup various components related to this specific XClass property
455    * type. See {@link com.xpn.xwiki.internal.objects.classes.PropertyClassProvider} for instance.
456    *
457    * @return an identifier for the data type of the property value (e.g. 'String', 'Number', 'Date')
458    */
 
459  103660 toggle public String getClassType()
460    {
461    // By default the hint is computed by removing the Class suffix, if present, from the Java simple class name
462    // (without the package). Subclasses can overwrite this method to use a different hint format.
463  103660 return StringUtils.removeEnd(getClass().getSimpleName(), "Class");
464    }
465   
466    /**
467    * Sets the property class type.
468    *
469    * @param type the class type
470    * @deprecated since 4.3M1, the property class type cannot be modified
471    */
 
472  0 toggle @Deprecated
473    public void setClassType(String type)
474    {
475  0 LOGGER.warn("The property class type cannot be modified!");
476    }
477   
 
478  82074 toggle @Override
479    public PropertyClass clone()
480    {
481  82074 PropertyClass pclass = (PropertyClass) super.clone();
482  82074 pclass.setObject(getObject());
483  82074 return pclass;
484    }
485   
 
486  0 toggle @Override
487    public Element toXML(BaseClass bclass)
488    {
489  0 return toXML();
490    }
491   
 
492  0 toggle @Override
493    public Element toXML()
494    {
495  0 Element pel = new DOMElement(getName());
496   
497    // Iterate over values sorted by field name so that the values are
498    // exported to XML in a consistent order.
499  0 Iterator it = getSortedIterator();
500  0 while (it.hasNext()) {
501  0 BaseProperty bprop = (BaseProperty) it.next();
502  0 pel.add(bprop.toXML());
503    }
504  0 Element el = new DOMElement("classType");
505  0 String classType = getClassType();
506  0 if (this.getClass().getSimpleName().equals(classType + "Class")) {
507    // Keep exporting the full Java class name for old/default property types to avoid breaking the XAR format
508    // (to allow XClasses created with the current version of XWiki to be imported in an older version).
509  0 classType = this.getClass().getName();
510    }
511  0 el.addText(classType);
512  0 pel.add(el);
513  0 return pel;
514    }
515   
 
516  0 toggle @Override
517    public void fromXML(Element element) throws XWikiException
518    {
519  0 super.fromXML(element);
520    }
521   
 
522  0 toggle @Override
523    public String toFormString()
524    {
525  0 return toString();
526    }
527   
 
528  0 toggle public void initLazyCollections()
529    {
530    }
531   
 
532  2 toggle public boolean isUnmodifiable()
533    {
534  2 return (getIntValue("unmodifiable") == 1);
535    }
536   
 
537  61925 toggle public void setUnmodifiable(boolean unmodifiable)
538    {
539  61925 if (unmodifiable) {
540  885 setIntValue("unmodifiable", 1);
541    } else {
542  61040 setIntValue("unmodifiable", 0);
543    }
544    }
545   
546    /**
547    * See if this property is disabled or not. A disabled property should not be editable, but existing object values
548    * are still kept in the database.
549    *
550    * @return {@code true} if this property is disabled and should not be used, {@code false} otherwise
551    * @see #setDisabled(boolean)
552    * @since 2.4M2
553    */
 
554  4974 toggle public boolean isDisabled()
555    {
556  4974 return (getIntValue("disabled", 0) == 1);
557    }
558   
559    /**
560    * Disable or re-enable this property. A disabled property should not be editable, but existing object values are
561    * still kept in the database.
562    *
563    * @param disabled whether the property is disabled or not
564    * @see #isDisabled()
565    * @since 2.4M2
566    */
 
567  61039 toggle public void setDisabled(boolean disabled)
568    {
569  61039 if (disabled) {
570  0 setIntValue("disabled", 1);
571    } else {
572  61039 setIntValue("disabled", 0);
573    }
574    }
575   
 
576  663 toggle public BaseProperty fromStringArray(String[] strings)
577    {
578  663 return fromString(strings[0]);
579    }
580   
 
581  0 toggle public boolean isValidColumnTypes(Property hibprop)
582    {
583  0 return true;
584    }
585   
 
586  198 toggle @Override
587    public BaseProperty fromValue(Object value)
588    {
589  198 BaseProperty property = newProperty();
590  198 property.setValue(value);
591  198 return property;
592    }
593   
 
594  0 toggle @Override
595    public BaseProperty newProperty()
596    {
597  0 return new BaseProperty();
598    }
599   
 
600  104 toggle public void setValidationRegExp(String validationRegExp)
601    {
602  104 setStringValue("validationRegExp", validationRegExp);
603    }
604   
 
605  42 toggle public String getValidationRegExp()
606    {
607  42 return getStringValue("validationRegExp");
608    }
609   
 
610  0 toggle public String getValidationMessage()
611    {
612  0 return getStringValue("validationMessage");
613    }
614   
 
615  0 toggle public void setValidationMessage(String validationMessage)
616    {
617  0 setStringValue("validationMessage", validationMessage);
618    }
619   
 
620  42 toggle public boolean validateProperty(BaseProperty property, XWikiContext context)
621    {
622  42 String regexp = getValidationRegExp();
623  42 if ((regexp == null) || (regexp.trim().equals(""))) {
624  42 return true;
625    }
626   
627  0 String value = ((property == null) || (property.getValue() == null)) ? "" : property.getValue().toString();
628  0 try {
629  0 if (context.getUtil().match(regexp, value)) {
630  0 return true;
631    }
632  0 XWikiValidationStatus.addErrorToContext((getObject() == null) ? "" : getObject().getName(), getName(),
633    getTranslatedPrettyName(context), getValidationMessage(), context);
634   
635  0 return false;
636    } catch (Exception e) {
637  0 XWikiValidationStatus.addExceptionToContext((getObject() == null) ? "" : getObject().getName(), getName(),
638    e, context);
639   
640  0 return false;
641    }
642    }
643   
 
644  0 toggle @Override
645    public void flushCache()
646    {
647  0 this.cachedCustomDisplayer = null;
648    }
649   
650    /**
651    * Compares two property definitions based on their index number.
652    *
653    * @param other the other property definition to be compared with
654    * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
655    * the specified object.
656    * @see #getNumber()
657    * @since 2.4M2
658    */
 
659  796 toggle @Override
660    public int compareTo(PropertyClass other)
661    {
662  796 int result = this.getNumber() - other.getNumber();
663   
664    // This should never happen, but just to remove the randomness in case it does happen, also compare their names.
665  796 if (result == 0) {
666  2 result = this.getName().compareTo(other.getName());
667    }
668   
669  796 return result;
670    }
671   
 
672  0 toggle protected String getFullQueryPropertyName()
673    {
674  0 return "obj." + getName();
675    }
676   
677    /**
678    * Returns the current cached default custom displayer for the PropertyClass. The result will be cached and can be
679    * flushed using {@link #flushCache()}. If it returns the empty string, then there is no default custom displayer
680    * for this class.
681    *
682    * @param context the current request context
683    * @return An identifier for the location of a custom displayer. This can be {@code class} if there's custom display
684    * code specified in the class itself, {@code page:currentwiki:XWiki.BooleanDisplayer} if such a document
685    * exists in the current wiki, {@code page:xwiki:XWiki.StringDisplayer} if such a document exists in the
686    * main wiki, or {@code template:displayer_boolean.vm} if a template on the filesystem or in the current
687    * skin exists.
688    */
 
689  2024 toggle protected String getCachedDefaultCustomDisplayer(XWikiContext context)
690    {
691    // First look at custom displayer in class. We should not cache this one.
692  2024 String customDisplay = getCustomDisplay();
693  2024 if (StringUtils.isNotEmpty(customDisplay)) {
694  82 return CLASS_DISPLAYER_IDENTIFIER;
695    }
696   
697    // Then look for pages or templates
698  1942 if (this.cachedCustomDisplayer == null) {
699  1708 this.cachedCustomDisplayer = getDefaultCustomDisplayer(getTypeName(), context);
700    }
701  1942 return this.cachedCustomDisplayer;
702    }
703   
704    /**
705    * Method to find the default custom displayer to use for a specific Property Class.
706    *
707    * @param propertyClassName the type of the property; this is defined in each subclass, such as {@code boolean},
708    * {@code string} or {@code dblist}
709    * @param context the current request context
710    * @return An identifier for the location of a custom displayer. This can be {@code class} if there's custom display
711    * code specified in the class itself, {@code page:currentwiki:XWiki.BooleanDisplayer} if such a document
712    * exists in the current wiki, {@code page:xwiki:XWiki.StringDisplayer} if such a document exists in the
713    * main wiki, or {@code template:displayer_boolean.vm} if a template on the filesystem or in the current
714    * skin exists.
715    */
 
716  1708 toggle protected String getDefaultCustomDisplayer(String propertyClassName, XWikiContext context)
717    {
718  1708 LOGGER.debug("Looking up default custom displayer for property class name [{}]", propertyClassName);
719   
720  1708 try {
721    // First look into the current wiki
722  1708 String pageName = StringUtils.capitalize(propertyClassName) + "Displayer";
723  1708 DocumentReference reference = new DocumentReference(context.getWikiId(), "XWiki", pageName);
724  1708 if (context.getWiki().exists(reference, context)) {
725  0 LOGGER.debug("Found default custom displayer for property class name in local wiki: [{}]", pageName);
726  0 return DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX + "XWiki." + pageName;
727    }
728   
729    // Look in the main wiki
730  1708 if (!context.isMainWiki()) {
731  0 reference = new DocumentReference(context.getMainXWiki(), "XWiki", pageName);
732  0 if (context.getWiki().exists(reference, context)) {
733  0 LOGGER.debug("Found default custom displayer for property class name in main wiki: [{}]", pageName);
734  0 return DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX + context.getMainXWiki() + ":XWiki." + pageName;
735    }
736    }
737   
738    // Look in templates
739  1708 String templateName = "displayer_" + propertyClassName + ".vm";
740  1708 TemplateManager templateManager = Utils.getComponent(TemplateManager.class);
741  1707 Template existingTemplate = templateManager.getTemplate(templateName);
742  1707 if (existingTemplate != null) {
743  16 LOGGER.debug("Found default custom displayer for property class name as template: [{}]", templateName);
744  16 return TEMPLATE_DISPLAYER_IDENTIFIER_PREFIX + templateName;
745    }
746    } catch (Throwable e) {
747    // If we fail we consider there is no custom displayer
748  1 LOGGER.error("Error while trying to evaluate if a property has a custom displayer", e);
749    }
750   
751  1692 return null;
752    }
753   
754    /**
755    * Get a short name identifying this type of property. This is derived from the java class name, lowercasing the
756    * part before {@code Class}.
757    *
758    * @return a string, for example {@code string}, {@code dblist}, {@code number}
759    */
 
760  1708 toggle private String getTypeName()
761    {
762  1708 return StringUtils.substringBeforeLast(this.getClass().getSimpleName(), "Class").toLowerCase();
763    }
764   
765    /**
766    * Apply a 3 ways merge on passed current, previous and new version of the same property. The passed current version
767    * is modified as result of the merge.
768    *
769    * @param currentProperty the current version of the element and the one to modify
770    * @param previousProperty the previous version of the element
771    * @param newProperty the new version of the property
772    * @param configuration the configuration of the merge Indicate how to deal with some conflicts use cases, etc.
773    * @param context the XWiki context
774    * @param mergeResult the merge report
775    * @since 6.2M1
776    */
 
777  0 toggle public <T extends EntityReference> void mergeProperty(BaseProperty<T> currentProperty,
778    BaseProperty<T> previousProperty, BaseProperty<T> newProperty, MergeConfiguration configuration,
779    XWikiContext context, MergeResult mergeResult)
780    {
781  0 currentProperty.merge(previousProperty, newProperty, configuration, context, mergeResult);
782    }
783    }