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

File PropertyClass.java

 

Coverage histogram

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

Code metrics

66
207
72
1
840
553
112
0.54
2.88
72
1.56

Classes

Class Line # Actions
PropertyClass 62 207 0% 112 113
0.672463867.2%
 

Contributing tests

This file is covered by 131 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   
24    import javax.script.ScriptContext;
25   
26    import org.apache.commons.lang3.StringUtils;
27    import org.apache.ecs.xhtml.input;
28    import org.dom4j.Element;
29    import org.dom4j.dom.DOMElement;
30    import org.hibernate.mapping.Property;
31    import org.slf4j.Logger;
32    import org.slf4j.LoggerFactory;
33    import org.xwiki.model.reference.ClassPropertyReference;
34    import org.xwiki.model.reference.DocumentReference;
35    import org.xwiki.model.reference.EntityReference;
36    import org.xwiki.script.ScriptContextManager;
37    import org.xwiki.security.authorization.AuthorExecutor;
38    import org.xwiki.stability.Unstable;
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: c3c7e7da4de1adc26f0ed8de82a5a1232b85a633 $
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  5 toggle public PropertyClass()
94    {
95    }
96   
 
97  153780 toggle public PropertyClass(String name, String prettyname, PropertyMetaClass xWikiClass)
98    {
99  153780 setName(name);
100  153780 setPrettyName(prettyname);
101  153780 setxWikiClass(xWikiClass);
102  153780 setUnmodifiable(false);
103  153780 setDisabled(false);
104    }
105   
 
106  10222 toggle @Override
107    protected ClassPropertyReference createReference()
108    {
109  10222 return new ClassPropertyReference(getName(), this.xclass.getReference());
110    }
111   
 
112  18282 toggle @Override
113    public BaseClass getXClass(XWikiContext context)
114    {
115  18282 return getxWikiClass();
116    }
117   
 
118  18323 toggle public BaseClass getxWikiClass()
119    {
120  18323 if (this.pMetaClass == null) {
121  948 MetaClass metaClass = MetaClass.getMetaClass();
122  948 this.pMetaClass = (PropertyMetaClass) metaClass.get(getClassType());
123    }
124  18323 return this.pMetaClass;
125    }
126   
 
127  153779 toggle public void setxWikiClass(BaseClass xWikiClass)
128    {
129  153780 this.pMetaClass = (PropertyMetaClass) xWikiClass;
130    }
131   
 
132  1007186 toggle @Override
133    public BaseCollection getObject()
134    {
135  1007186 return this.xclass;
136    }
137   
 
138  990960 toggle @Override
139    public void setObject(BaseCollection object)
140    {
141  990960 this.xclass = (BaseClass) object;
142    }
143   
 
144  21815 toggle public String getFieldFullName()
145    {
146  21815 if (getObject() == null) {
147  2634 return getName();
148    }
149  19181 return getObject().getName() + "_" + getName();
150    }
151   
 
152  265952 toggle @Override
153    public long getId()
154    {
155  265952 if (getObject() == null) {
156  0 return this.id;
157    }
158  265952 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  85 toggle @Override
186    public void displayHidden(StringBuffer buffer, String name, String prefix, BaseCollection object,
187    XWikiContext context)
188    {
189  85 input input = new input();
190  85 input.setAttributeFilter(new XMLAttributeValueFilter());
191  85 BaseProperty prop = (BaseProperty) object.safeget(name);
192  85 if (prop != null) {
193  85 input.setValue(prop.toText());
194    }
195   
196  85 input.setType("hidden");
197  85 input.setName(prefix + name);
198  85 input.setID(prefix + name);
199  85 buffer.append(input.toString());
200    }
201   
 
202  8854 toggle @Override
203    public void displayView(StringBuffer buffer, String name, String prefix, BaseCollection object,
204    XWikiContext context)
205    {
206  8854 BaseProperty prop = (BaseProperty) object.safeget(name);
207  8854 if (prop != null) {
208  8854 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  11 toggle public String displayHidden(String name, String prefix, BaseCollection object, XWikiContext context)
232    {
233  11 StringBuffer buffer = new StringBuffer();
234  11 displayHidden(buffer, name, prefix, object, context);
235  11 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  2018 toggle public String displayView(String name, String prefix, BaseCollection object, XWikiContext context)
244    {
245  2018 StringBuffer buffer = new StringBuffer();
246  2018 displayView(buffer, name, prefix, object, context);
247  2018 return buffer.toString();
248    }
249   
 
250  915 toggle public String displayView(String name, BaseCollection object, XWikiContext context)
251    {
252  915 return displayView(name, "", object, context);
253    }
254   
 
255  7405 toggle public String displayEdit(String name, String prefix, BaseCollection object, XWikiContext context)
256    {
257  7405 StringBuffer buffer = new StringBuffer();
258  7405 displayEdit(buffer, name, prefix, object, context);
259  7405 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  19136 toggle public boolean isCustomDisplayed(XWikiContext context)
268    {
269  19136 return (StringUtils.isNotEmpty(getCachedDefaultCustomDisplayer(context)));
270    }
271   
 
272  1012 toggle public void displayCustom(StringBuffer buffer, String fieldName, String prefix, String type, BaseObject object,
273    final XWikiContext context) throws XWikiException
274    {
275  1012 String content = "";
276  1012 try {
277  1012 ScriptContext scontext = Utils.getComponent(ScriptContextManager.class).getCurrentScriptContext();
278  1012 scontext.setAttribute("name", fieldName, ScriptContext.ENGINE_SCOPE);
279  1012 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  1012 scontext.setAttribute("field", new com.xpn.xwiki.api.PropertyClass(this, context),
285    ScriptContext.ENGINE_SCOPE);
286  1012 scontext.setAttribute("object", new com.xpn.xwiki.api.Object(object, context), ScriptContext.ENGINE_SCOPE);
287  1012 scontext.setAttribute("type", type, ScriptContext.ENGINE_SCOPE);
288   
289  1012 BaseProperty prop = (BaseProperty) object.safeget(fieldName);
290  1012 if (prop != null) {
291  992 scontext.setAttribute("value", prop.getValue(), ScriptContext.ENGINE_SCOPE);
292    } else {
293    // The $value property can exist in the velocity context, we overwrite it to make sure we don't get a
294    // wrong value in the displayer when the property does not exist yet.
295  20 scontext.setAttribute("value", null, ScriptContext.ENGINE_SCOPE);
296    }
297   
298  1012 String customDisplayer = getCachedDefaultCustomDisplayer(context);
299  1012 if (StringUtils.isNotEmpty(customDisplayer)) {
300  1012 if (customDisplayer.equals(CLASS_DISPLAYER_IDENTIFIER)) {
301  154 final String rawContent = getCustomDisplay();
302  154 XWikiDocument classDocument =
303    context.getWiki().getDocument(getObject().getDocumentReference(), context);
304  154 final String classSyntax = classDocument.getSyntax().toIdString();
305    // Using author reference since the document content is not relevant in this case.
306  154 DocumentReference authorReference = classDocument.getAuthorReference();
307  154 if (authorReference == null && classDocument.isNew()) {
308    // If the class document has not been saved yet (e.g. we could be previewing a class property in
309    // the class editor) then use the context user as author (e.g. the user that is in the process
310    // of creating the class).
311  4 authorReference = context.getUserReference();
312    }
313   
314    // Make sure we render the custom displayer with the rights of the user who wrote it (i.e. class
315    // document author).
316  154 content = renderContentInContext(rawContent, classSyntax, authorReference,
317    classDocument.getDocumentReference(), context);
318  858 } else if (customDisplayer.startsWith(DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX)) {
319  0 XWikiDocument displayerDoc = context.getWiki().getDocument(
320    StringUtils.substringAfter(customDisplayer, DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX), context);
321  0 final String rawContent = displayerDoc.getContent();
322  0 final String displayerDocSyntax = displayerDoc.getSyntax().toIdString();
323  0 DocumentReference authorReference = displayerDoc.getContentAuthorReference();
324   
325    // Make sure we render the custom displayer with the rights of the user who wrote it (i.e. displayer
326    // document content author).
327  0 content = renderContentInContext(rawContent, displayerDocSyntax, authorReference,
328    displayerDoc.getDocumentReference(), context);
329  858 } else if (customDisplayer.startsWith(TEMPLATE_DISPLAYER_IDENTIFIER_PREFIX)) {
330  858 content = context.getWiki().evaluateTemplate(
331    StringUtils.substringAfter(customDisplayer, TEMPLATE_DISPLAYER_IDENTIFIER_PREFIX), context);
332    }
333    }
334    } catch (Exception e) {
335  0 throw new XWikiException(XWikiException.MODULE_XWIKI_CLASSES,
336    XWikiException.ERROR_XWIKI_CLASSES_CANNOT_PREPARE_CUSTOM_DISPLAY,
337    "Exception while preparing the custom display of " + fieldName, e, null);
338   
339    }
340  1012 buffer.append(content);
341    }
342   
343    /**
344    * Render content in the current document's context with the rights of the given user.
345    *
346    * @since 8.3M2
347    * @deprecated since 10.11RC1, use
348    * {@link #renderContentInContext(String, String, DocumentReference, DocumentReference, XWikiContext)}
349    * instead
350    */
 
351  0 toggle @Deprecated
352    protected String renderContentInContext(final String content, final String syntax,
353    DocumentReference authorReference, final XWikiContext context) throws Exception
354    {
355  0 return renderContentInContext(content, syntax, authorReference, null, context);
356    }
357   
358    /**
359    * Render content in the current document's context with the rights of the given user.
360    *
361    * @since 10.11RC1
362    */
 
363  154 toggle @Unstable
364    protected String renderContentInContext(final String content, final String syntax,
365    DocumentReference authorReference, DocumentReference secureDocument, final XWikiContext context)
366    throws Exception
367    {
368  154 return Utils.getComponent(AuthorExecutor.class)
369    .call(() -> context.getDoc().getRenderedContent(content, syntax, context), authorReference, secureDocument);
370    }
371   
 
372  0 toggle @Override
373    public String getClassName()
374    {
375  0 BaseClass bclass = getxWikiClass();
376  0 return (bclass == null) ? "" : bclass.getName();
377    }
378   
379    // In property classes we need to store this info in the HashMap for fields
380    // This way it is readable by the displayEdit/displayView functions..
 
381  2182024 toggle @Override
382    public String getName()
383    {
384  2182023 return getStringValue("name");
385    }
386   
 
387  299720 toggle @Override
388    public void setName(String name)
389    {
390  299720 setStringValue("name", name);
391    }
392   
 
393  20304 toggle public String getCustomDisplay()
394    {
395  20304 return getStringValue("customDisplay");
396    }
397   
 
398  3 toggle public void setCustomDisplay(String value)
399    {
400  3 setLargeStringValue("customDisplay", value);
401    }
402   
 
403  492790 toggle @Override
404    public String getPrettyName()
405    {
406  492790 return getStringValue("prettyName");
407    }
408   
 
409  3065 toggle public String getPrettyName(XWikiContext context)
410    {
411  3065 return getTranslatedPrettyName(context);
412    }
413   
 
414  7020 toggle public String getTranslatedPrettyName(XWikiContext context)
415    {
416  7020 String msgName = getFieldFullName();
417  7020 if ((context == null) || (context.getWiki() == null)) {
418  0 return getPrettyName();
419    }
420   
421  7020 String prettyName = localizePlain(msgName);
422  7020 if (prettyName == null) {
423  1152 return getPrettyName();
424    }
425  5868 return prettyName;
426    }
427   
 
428  642840 toggle @Override
429    public void setPrettyName(String prettyName)
430    {
431  642841 setStringValue("prettyName", prettyName);
432    }
433   
434    /**
435    * @param property name of the property
436    * @return the localized value of the property, with a fallback to the inner value
437    */
 
438  2735 toggle private String getLocalizedPropertyValue(String property)
439    {
440  2735 String propertyName = String.format("%s_%s", getFieldFullName(), property);
441  2735 String propertyValue = localizePlain(propertyName);
442  2735 if (propertyValue == null) {
443  2715 propertyName = getLargeStringValue(property);
444  2715 if (StringUtils.isNotBlank(propertyName)) {
445  0 propertyValue = localizePlainOrKey(propertyName, propertyName);
446    }
447    }
448  2735 return propertyValue;
449    }
450   
451    /**
452    * Get the localized hint. A hint is a text displayed in the object editor to help the user filling some content.
453    *
454    * @return the localized hint.
455    * @since 9.11RC1
456    */
 
457  2735 toggle public String getHint()
458    {
459  2735 return getLocalizedPropertyValue("hint");
460    }
461   
462    /**
463    * Set the text displayed in the object editor to help the user filling some content.
464    *
465    * @since 9.11RC1
466    */
 
467  0 toggle public void setHint(String hint)
468    {
469  0 setLargeStringValue("hint", hint);
470    }
471   
 
472  0 toggle public String getTooltip()
473    {
474  0 return getLargeStringValue("tooltip");
475    }
476   
477    /**
478    * Gets international tooltip
479    *
480    * @param context
481    * @return
482    */
 
483  0 toggle public String getTooltip(XWikiContext context)
484    {
485  0 return getLocalizedPropertyValue("tooltip");
486    }
487   
 
488  0 toggle public void setTooltip(String tooltip)
489    {
490  0 setLargeStringValue("tooltip", tooltip);
491    }
492   
 
493  780542 toggle @Override
494    public int getNumber()
495    {
496  780543 return getIntValue("number");
497    }
498   
 
499  489106 toggle @Override
500    public void setNumber(int number)
501    {
502  489106 setIntValue("number", number);
503    }
504   
505    /**
506    * Each type of XClass property is identified by a string that specifies the data type of the property value (e.g.
507    * 'String', 'Number', 'Date') without disclosing implementation details. The internal implementation of an XClass
508    * property type can change over time but its {@code classType} should not.
509    * <p>
510    * The {@code classType} can be used as a hint to lookup various components related to this specific XClass property
511    * type. See {@link com.xpn.xwiki.internal.objects.classes.PropertyClassProvider} for instance.
512    *
513    * @return an identifier for the data type of the property value (e.g. 'String', 'Number', 'Date')
514    */
 
515  989492 toggle public String getClassType()
516    {
517    // By default the hint is computed by removing the Class suffix, if present, from the Java simple class name
518    // (without the package). Subclasses can overwrite this method to use a different hint format.
519  989492 return StringUtils.removeEnd(getClass().getSimpleName(), "Class");
520    }
521   
522    /**
523    * Sets the property class type.
524    *
525    * @param type the class type
526    * @deprecated since 4.3M1, the property class type cannot be modified
527    */
 
528  0 toggle @Deprecated
529    public void setClassType(String type)
530    {
531  0 LOGGER.warn("The property class type cannot be modified!");
532    }
533   
 
534  432732 toggle @Override
535    public PropertyClass clone()
536    {
537  432732 PropertyClass pclass = (PropertyClass) super.clone();
538  432732 pclass.setObject(getObject());
539  432732 return pclass;
540    }
541   
 
542  0 toggle @Override
543    public Element toXML(BaseClass bclass)
544    {
545  0 return toXML();
546    }
547   
 
548  0 toggle @Override
549    public Element toXML()
550    {
551  0 Element pel = new DOMElement(getName());
552   
553    // Iterate over values sorted by field name so that the values are
554    // exported to XML in a consistent order.
555  0 Iterator it = getSortedIterator();
556  0 while (it.hasNext()) {
557  0 BaseProperty bprop = (BaseProperty) it.next();
558  0 pel.add(bprop.toXML());
559    }
560  0 Element el = new DOMElement("classType");
561  0 String classType = getClassType();
562  0 if (this.getClass().getSimpleName().equals(classType + "Class")) {
563    // Keep exporting the full Java class name for old/default property types to avoid breaking the XAR format
564    // (to allow XClasses created with the current version of XWiki to be imported in an older version).
565  0 classType = this.getClass().getName();
566    }
567  0 el.addText(classType);
568  0 pel.add(el);
569  0 return pel;
570    }
571   
 
572  0 toggle @Override
573    public void fromXML(Element element) throws XWikiException
574    {
575  0 super.fromXML(element);
576    }
577   
 
578  0 toggle @Override
579    public String toFormString()
580    {
581  0 return toString();
582    }
583   
 
584  0 toggle public void initLazyCollections()
585    {
586    }
587   
 
588  2 toggle public boolean isUnmodifiable()
589    {
590  2 return (getIntValue("unmodifiable") == 1);
591    }
592   
 
593  155467 toggle public void setUnmodifiable(boolean unmodifiable)
594    {
595  155467 if (unmodifiable) {
596  1688 setIntValue("unmodifiable", 1);
597    } else {
598  153779 setIntValue("unmodifiable", 0);
599    }
600    }
601   
602    /**
603    * See if this property is disabled or not. A disabled property should not be editable, but existing object values
604    * are still kept in the database.
605    *
606    * @return {@code true} if this property is disabled and should not be used, {@code false} otherwise
607    * @see #setDisabled(boolean)
608    * @since 2.4M2
609    */
 
610  18355 toggle public boolean isDisabled()
611    {
612  18355 return (getIntValue("disabled", 0) == 1);
613    }
614   
615    /**
616    * Disable or re-enable this property. A disabled property should not be editable, but existing object values are
617    * still kept in the database.
618    *
619    * @param disabled whether the property is disabled or not
620    * @see #isDisabled()
621    * @since 2.4M2
622    */
 
623  153779 toggle public void setDisabled(boolean disabled)
624    {
625  153779 if (disabled) {
626  0 setIntValue("disabled", 1);
627    } else {
628  153780 setIntValue("disabled", 0);
629    }
630    }
631   
 
632  2442 toggle public BaseProperty fromStringArray(String[] strings)
633    {
634  2442 return fromString(strings[0]);
635    }
636   
 
637  0 toggle public boolean isValidColumnTypes(Property hibprop)
638    {
639  0 return true;
640    }
641   
 
642  9294 toggle @Override
643    public BaseProperty fromValue(Object value)
644    {
645  9294 BaseProperty property = newProperty();
646  9294 property.setValue(value);
647  9294 return property;
648    }
649   
 
650  0 toggle @Override
651    public BaseProperty newProperty()
652    {
653  0 return new BaseProperty();
654    }
655   
 
656  297 toggle public void setValidationRegExp(String validationRegExp)
657    {
658  297 setStringValue("validationRegExp", validationRegExp);
659    }
660   
 
661  45 toggle public String getValidationRegExp()
662    {
663  45 return getStringValue("validationRegExp");
664    }
665   
 
666  1 toggle public String getValidationMessage()
667    {
668  1 return getStringValue("validationMessage");
669    }
670   
 
671  0 toggle public void setValidationMessage(String validationMessage)
672    {
673  0 setStringValue("validationMessage", validationMessage);
674    }
675   
 
676  45 toggle public boolean validateProperty(BaseProperty property, XWikiContext context)
677    {
678  45 String regexp = getValidationRegExp();
679  45 if ((regexp == null) || (regexp.trim().equals(""))) {
680  42 return true;
681    }
682   
683  3 String value = ((property == null) || (property.getValue() == null)) ? "" : property.getValue().toString();
684  3 try {
685  3 if (context.getUtil().match(regexp, value)) {
686  2 return true;
687    }
688  1 XWikiValidationStatus.addErrorToContext((getObject() == null) ? "" : getObject().getName(), getName(),
689    getTranslatedPrettyName(context), getValidationMessage(), context);
690   
691  1 return false;
692    } catch (Exception e) {
693  0 XWikiValidationStatus.addExceptionToContext((getObject() == null) ? "" : getObject().getName(), getName(),
694    e, context);
695   
696  0 return false;
697    }
698    }
699   
 
700  565 toggle @Override
701    public void flushCache()
702    {
703  565 this.cachedCustomDisplayer = null;
704    }
705   
706    /**
707    * Compares two property definitions based on their index number.
708    *
709    * @param other the other property definition to be compared with
710    * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
711    * the specified object.
712    * @see #getNumber()
713    * @since 2.4M2
714    */
 
715  810 toggle @Override
716    public int compareTo(PropertyClass other)
717    {
718  810 int result = this.getNumber() - other.getNumber();
719   
720    // This should never happen, but just to remove the randomness in case it does happen, also compare their names.
721  810 if (result == 0) {
722  4 result = this.getName().compareTo(other.getName());
723    }
724   
725  810 return result;
726    }
727   
 
728  0 toggle protected String getFullQueryPropertyName()
729    {
730  0 return "obj." + getName();
731    }
732   
733    /**
734    * Returns the current cached default custom displayer for the PropertyClass. The result will be cached and can be
735    * flushed using {@link #flushCache()}. If it returns the empty string, then there is no default custom displayer
736    * for this class.
737    *
738    * @param context the current request context
739    * @return An identifier for the location of a custom displayer. This can be {@code class} if there's custom display
740    * code specified in the class itself, {@code page:currentwiki:XWiki.BooleanDisplayer} if such a document
741    * exists in the current wiki, {@code page:xwiki:XWiki.StringDisplayer} if such a document exists in the
742    * main wiki, or {@code template:displayer_boolean.vm} if a template on the filesystem or in the current
743    * skin exists.
744    */
 
745  20148 toggle protected String getCachedDefaultCustomDisplayer(XWikiContext context)
746    {
747    // First look at custom displayer in class. We should not cache this one.
748  20148 String customDisplay = getCustomDisplay();
749  20148 if (StringUtils.isNotEmpty(customDisplay)) {
750  305 return CLASS_DISPLAYER_IDENTIFIER;
751    }
752   
753    // Then look for pages or templates
754  19843 if (this.cachedCustomDisplayer == null) {
755  18292 this.cachedCustomDisplayer = getDefaultCustomDisplayer(getTypeName(), context);
756    }
757  19843 return this.cachedCustomDisplayer;
758    }
759   
760    /**
761    * Method to find the default custom displayer to use for a specific Property Class.
762    *
763    * @param propertyClassName the type of the property; this is defined in each subclass, such as {@code boolean},
764    * {@code string} or {@code dblist}
765    * @param context the current request context
766    * @return An identifier for the location of a custom displayer. This can be {@code class} if there's custom display
767    * code specified in the class itself, {@code page:currentwiki:XWiki.BooleanDisplayer} if such a document
768    * exists in the current wiki, {@code page:xwiki:XWiki.StringDisplayer} if such a document exists in the
769    * main wiki, or {@code template:displayer_boolean.vm} if a template on the filesystem or in the current
770    * skin exists.
771    */
 
772  18292 toggle protected String getDefaultCustomDisplayer(String propertyClassName, XWikiContext context)
773    {
774  18292 LOGGER.debug("Looking up default custom displayer for property class name [{}]", propertyClassName);
775   
776  18292 try {
777    // First look into the current wiki
778  18292 String pageName = StringUtils.capitalize(propertyClassName) + "Displayer";
779  18292 DocumentReference reference = new DocumentReference(context.getWikiId(), "XWiki", pageName);
780  18292 if (context.getWiki().exists(reference, context)) {
781  0 LOGGER.debug("Found default custom displayer for property class name in local wiki: [{}]", pageName);
782  0 return DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX + "XWiki." + pageName;
783    }
784   
785    // Look in the main wiki
786  18292 if (!context.isMainWiki()) {
787  0 reference = new DocumentReference(context.getMainXWiki(), "XWiki", pageName);
788  0 if (context.getWiki().exists(reference, context)) {
789  0 LOGGER.debug("Found default custom displayer for property class name in main wiki: [{}]", pageName);
790  0 return DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX + context.getMainXWiki() + ":XWiki." + pageName;
791    }
792    }
793   
794    // Look in templates
795  18292 String templateName = "displayer_" + propertyClassName + ".vm";
796  18292 TemplateManager templateManager = Utils.getComponent(TemplateManager.class);
797  18292 Template existingTemplate = templateManager.getTemplate(templateName);
798  18292 if (existingTemplate != null) {
799  165 LOGGER.debug("Found default custom displayer for property class name as template: [{}]", templateName);
800  165 return TEMPLATE_DISPLAYER_IDENTIFIER_PREFIX + templateName;
801    }
802    } catch (Throwable e) {
803    // If we fail we consider there is no custom displayer
804  0 LOGGER.error("Error finding if property [{}] has a custom displayer. "
805    + "Considering that there's no custom displayer.", propertyClassName, e);
806    }
807   
808  18127 return null;
809    }
810   
811    /**
812    * Get a short name identifying this type of property. This is derived from the java class name, lowercasing the
813    * part before {@code Class}.
814    *
815    * @return a string, for example {@code string}, {@code dblist}, {@code number}
816    */
 
817  18292 toggle private String getTypeName()
818    {
819  18292 return StringUtils.substringBeforeLast(this.getClass().getSimpleName(), "Class").toLowerCase();
820    }
821   
822    /**
823    * Apply a 3 ways merge on passed current, previous and new version of the same property. The passed current version
824    * is modified as result of the merge.
825    *
826    * @param currentProperty the current version of the element and the one to modify
827    * @param previousProperty the previous version of the element
828    * @param newProperty the new version of the property
829    * @param configuration the configuration of the merge Indicate how to deal with some conflicts use cases, etc.
830    * @param context the XWiki context
831    * @param mergeResult the merge report
832    * @since 6.2M1
833    */
 
834  0 toggle public <T extends EntityReference> void mergeProperty(BaseProperty<T> currentProperty,
835    BaseProperty<T> previousProperty, BaseProperty<T> newProperty, MergeConfiguration configuration,
836    XWikiContext context, MergeResult mergeResult)
837    {
838  0 currentProperty.merge(previousProperty, newProperty, configuration, context, mergeResult);
839    }
840    }