Wiki source code of Java Code Style

Last modified by Alexandru Brassat on 2026/01/19 21:45

Hide last authors
Manuel Smeria 19.2 1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
Vincent Massol 2.1 4
Paul Libbrecht 40.1 5 The XWiki project is following a specific coding style for Java code. We're using [[Checkstyle>>http://checkstyle.sourceforge.net/]] ([[checkstyle.xml>>https://raw.github.com/xwiki/xwiki-commons/master/xwiki-commons-tools/xwiki-commons-tool-verification-resources/src/main/resources/checkstyle.xml]]) to ensure compliance of the code. Our build (the Maven one) is configured to fail on violations. This is part of the [[automated checks>>https://dev.xwiki.org/xwiki/bin/view/Community/Building/#HAutomaticChecks]], see there for ways to skip if necessary at times. However the decision to follow this code style and enforce it was only made long after the beginning of the project and not all the code base has been moved to this new code style. Hence:
Vincent Massol 1.1 6
Vincent Massol 2.1 7 * We're only enforcing the code style in the code that has been moved to the new code style. The checked files are defined in //xwiki/core/pom.xml// (bottom of file).
8 * We're asking new code to follow the new style and then once a Java file is compliant, to edit //xwiki/core/pom.xml// and add it there so that we cannot regress...
9
Thomas Mortagne 12.1 10 For examples of "clean" class see the following example and its unit tests:
Vincent Massol 1.1 11
Nikita Petrenko 67.2 12 * DefaultObservationManager.java ([[main sources>>https://github.com/xwiki/xwiki-commons/blob/master/xwiki-commons-core/xwiki-commons-observation/xwiki-commons-observation-local/src/main/java/org/xwiki/observation/internal/DefaultObservationManager.java]]|[[tests>>https://github.com/xwiki/xwiki-commons/blob/master/xwiki-commons-core/xwiki-commons-observation/xwiki-commons-observation-local/src/test/java/org/xwiki/observation/internal/DefaultObservationManagerTest.java]])
Vincent Massol 1.1 13
Vincent Massol 3.1 14 = Configuring your IDE to use the XWiki code style =
Vincent Massol 1.1 15
Thomas Mortagne 47.1 16 == Eclipse ==
Vincent Massol 1.1 17
Manuel Smeria 19.2 18 Download [[codestyle-eclipse-java.xml>>https://raw.github.com/xwiki/xwiki-commons/master/xwiki-commons-tools/xwiki-commons-tool-verification-resources/src/main/resources/codestyle-eclipse-java.xml]].
Vincent Massol 1.1 19
Vincent Massol 2.1 20 After this, select //Window > Preferences//, and open up the configuration for //Java > Code Style > Code Formatter//. Click on the button labeled //Import...// and select the file you downloaded. Give the style a name, and click OK.
Vincent Massol 1.1 21
Vincent Massol 2.1 22 To reformat a file, press ##Ctrl+Shift+F## while inside that file. To format only a portion of the file, select it and press the same key combination.
Vincent Massol 1.1 23
Manuel Smeria 19.2 24 Download [[codetemplates-eclipse.xml>>https://raw.github.com/xwiki/xwiki-commons/master/xwiki-commons-tools/xwiki-commons-tool-verification-resources/src/main/resources/codetemplates-eclipse.xml]].
Vincent Massol 1.1 25
Vincent Massol 2.1 26 After this, select //Window > Preferences//, and open up the configuration for //Java > Code Style > Code Templates//. Click on the button labeled //Import...// and select the file you downloaded. You can enable "Automatically add comments for new methods and types" if you want.
Vincent Massol 1.1 27
Vincent Massol 2.1 28 To generate a javadoc, press ##Meta+Shift+J## while on the element you want to document.
Vincent Massol 1.1 29
Vincent Massol 44.1 30 == IntelliJ IDEA ==
Vincent Massol 1.1 31
Vincent Massol 44.1 32 * Set up IntelliJ IDEA code styles:
33 * Download [[codestyle-idea.xml>>https://raw.github.com/xwiki/xwiki-commons/master/xwiki-commons-tools/xwiki-commons-tool-verification-resources/src/main/resources/codestyle-idea.xml]].
Eduard Moraru 49.1 34 * Go to IntelliJ's ##File > Settings## and to ##Editor > Code Style## and select ##Import Scheme > IntelliJ IDEA Code Style XML## as shown on(((
Vincent Massol 45.1 35 {{image reference="intellij-idea-import-styles.png" width="350px"/}}
Vincent Massol 27.1 36 )))
Vincent Massol 44.1 37 * Set up File Templates (used when creating a new Java class, Interface, etc):(((
Vincent Massol 27.1 38 Download [[idea-fileTemplates-xwiki.tar.gz>>attach:idea-fileTemplates-xwiki.tar.gz]].
39
Eduard Moraru 49.1 40 Close IntelliJ IDEA. Ungzip and untar the file in the [[configuration directory>>https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#config-directory]] (or copy unzipped "fileTemplates" directory in the following location):
Clément Aubin 28.2 41
Eduard Moraru 49.1 42 * For Mac: in ##~~/Library/Application Support/JetBrains/<IDEA VERSION>##
Nikita Petrenko 68.3 43 * For Linux: in case if installed from JetBrains website in ##~~/.config/JetBrains/<IDEA VERSION>## or if installed from Linux Software app in ##~~/.var/app/com.jetbrains.IntelliJ-IDEA-Community/config/JetBrains/<IDEA VERSION>##
Eduard Moraru 49.2 44 * For Windows: in ##C:\Users\<username>\AppData\Roaming\JetBrains\<IDEA VERSION>##
Vincent Massol 27.1 45 )))
Neha Gupta 33.1 46 * If codestyle is not imported automatically, go to ##Other Settings > Default Settings > Java codestyle > Set from (XML) > select file downloaded above.##
47 * Restart Intellij IDEA.
Vincent Massol 27.1 48
Vincent Massol 58.2 49 {{info}}
Nikita Petrenko 67.3 50 It's recommended to add the CheckStyle and SonarLint (since 10.13 SonarLint becomes SonarQube for IDE) plugins in your IntelliJ IDEA instance.
Vincent Massol 58.2 51 {{/info}}
Lucas Charpentier 57.1 52
Vincent Massol 3.1 53 = Interface best practices =
Vincent Massol 1.1 54
Vincent Massol 3.1 55 == Do not use 'public' in interfaces ==
Vincent Massol 2.1 56
Vincent Massol 1.1 57 Public is always implied in interfaces. Do not write:
58
Vincent Massol 2.1 59 {{code}}
Vincent Massol 1.1 60 public interface Page
61 {
62 public String getParentSpaceKey();
63 }
Vincent Massol 2.1 64 {{/code}}
Vincent Massol 1.1 65
66 But instead, write
67
Vincent Massol 2.1 68 {{code}}
Vincent Massol 1.1 69 public interface Page
70 {
71 String getParentSpaceKey();
72 }
Vincent Massol 2.1 73 {{/code}}
Vincent Massol 1.1 74
Hassan Ali 41.1 75 == Make sure your code is backward compatible ==
76
Thomas Mortagne 55.1 77 Adding a new method to a public interface is a breaking change and your build will fail with error:
Hassan Ali 41.1 78
79 {{error}}
80 Method was added to an Interface.
81 {{/error}}
82
Thomas Mortagne 53.1 83 But it's possible to make a new interface method backward compatible by providing a default implementation (using the ##default## keyword as in):
Hassan Ali 41.1 84
85 {{code}}
Thomas Mortagne 54.1 86 @Unstable
Hassan Ali 41.1 87 default MethodType myNewMethod()
88 {
Thomas Mortagne 52.1 89 return myOtherMethod();
Hassan Ali 41.1 90 }
91 {{/code}}
92
Vincent Massol 3.1 93 = Javadoc Best Practices =
Vincent Massol 1.1 94
Vincent Massol 23.3 95 We are following the [[Oracle Javadoc coding conventions>>http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html#styleguide]]. Please make sure you're familiar with them when you write javadoc.
Vincent Massol 1.1 96
Vincent Massol 3.1 97 == Write useful comments ==
Vincent Massol 1.1 98
Manuel Smeria 19.2 99 Do not repeat the name of the method or any useless information. For example, if you have:
Vincent Massol 1.1 100
Vincent Massol 23.4 101 {{code language="java"}}
Vincent Massol 1.1 102 /**
103 * @return the id
104 */
105 public String getId()
106 {
107 return this.id;
108 }
Vincent Massol 2.1 109 {{/code}}
Vincent Massol 1.1 110
111 Instead, write:
112
Vincent Massol 23.4 113 {{code language="java"}}
Vincent Massol 1.1 114 /**
115 * @return the attachment id (the id is the filename of the XWikiAttachment object used to construct this Attachment object)
116 */
117 public String getId()
118 {
119 return this.id;
120 }
Vincent Massol 2.1 121 {{/code}}
Vincent Massol 1.1 122
Vincent Massol 3.1 123 == Do not duplicate Javadoc ==
Vincent Massol 1.1 124
Vincent Massol 13.1 125 If you inherit from an interface/class then you shouldn't copy the Javadoc from the super type. Instead you should reference it or add more fine-tuned explanations. For example if //getSomething()// is the implementation of a method defined in an inherited //Something// interface or parent class, you shouldn't write:
Vincent Massol 1.1 126
Vincent Massol 23.4 127 {{code language="java"}}
Vincent Massol 1.1 128 /**
129 * Do something blah blah.
130 */
131 public void doSomething()
132 {
133
134 [...]
Vincent Massol 2.1 135 {{/code}}
Vincent Massol 1.1 136
Vincent Massol 13.1 137 Instead, write either the following:
Vincent Massol 1.1 138
Vincent Massol 23.4 139 {{code language="java"}}
Vincent Massol 1.1 140 /**
141 * {@inheritDoc}
142 *
143 * <p>Optionally add here javadoc additional to the one inherited from the parent javadoc.</p>
144 */
Vincent Massol 13.1 145 @Override
Vincent Massol 1.1 146 public void doSomething()
147 {
148 [...]
Vincent Massol 2.1 149 {{/code}}
Vincent Massol 1.1 150
Vincent Massol 13.1 151 or (it you don't have anything else to add in the javadoc):
152
Vincent Massol 23.4 153 {{code language="java"}}
Vincent Massol 13.1 154 @Override
155 public void doSomething()
156 {
157 [...]
158 {{/code}}
159
Manuel Smeria 19.2 160 {{warning}}
161 Don't forget the {{{@Override}}} annotation!
162 {{/warning}}
Vincent Massol 13.1 163
Vincent Massol 3.1 164 == Do not duplicate method comments with parameters comments ==
Vincent Massol 1.1 165
166 Instead of writing:
167
Vincent Massol 23.4 168 {{code language="java"}}
Vincent Massol 1.1 169 /**
170 * Returns the key of the space to which this page belongs to.
171 *
172 * @return - Parent space's key as String.
173 */
174 public String getParentSpaceKey();
Vincent Massol 2.1 175 {{/code}}
Vincent Massol 1.1 176
177 Write:
178
Vincent Massol 23.4 179 {{code language="java"}}
Vincent Massol 1.1 180 /**
181 * @return the key of the space to which this page belongs to. For example "Main".
182 */
183 public String getParentSpaceKey();
Vincent Massol 2.1 184 {{/code}}
Vincent Massol 1.1 185
Vincent Massol 3.1 186 == Use version and since javadoc tags ==
Vincent Massol 1.1 187
188 For example:
189
Vincent Massol 23.4 190 {{code language="java"}}
Vincent Massol 1.1 191 /**
192 * Something, blah blah...
193 *
Thomas Mortagne 32.1 194 * @version $Id$
Thomas Mortagne 62.1 195 * @since 16.0.0RC1
Vincent Massol 1.1 196 */
Vincent Massol 2.1 197 {{/code}}
Vincent Massol 1.1 198
Manuel Smeria 19.2 199 {{warning}}
Vincent Massol 28.1 200 Do not use author javadoc tags! We don't have code ownership in XWiki. Everyone can modify any portion of code and all committers support all code.
Manuel Smeria 19.2 201 {{/warning}}
Vincent Massol 1.1 202
Vincent Massol 24.1 203 == Use one @since per version ==
204
Vincent Massol 29.2 205 When introducing a class or a new method on several branches, multiple {{code language="java"}}@since{{/code}} annotations must be used to mention the different versions in which this element has been added.
Clément Aubin 29.1 206
Vincent Massol 24.1 207 For example:
208
209 {{code language="java"}}
210 [...]
211 * @since 7.4.5
212 * @since 8.2.2
Thomas Mortagne 61.1 213 * @since 16.0.0RC1
Vincent Massol 24.1 214 */
215 {{/code}}
216
Thomas Mortagne 61.1 217 (and not {{code language="java"}}@since 7.4.5, 8.2.2, 16.0.0RC1{{/code}})
Vincent Massol 24.1 218
Vincent Massol 30.1 219 == Use the right format for examples ==
220
221 If you need to use example and have what you past be not modified, use the ##{@code}}## construct, as in:
222
223 {{code language="java"}}
224 * <pre>{@code
225 * <plugin>
226 * <groupId>org.xwiki.platform</groupId>
227 * <artifactId>xwiki-platform-tool-provision-plugin</artifactId>
228 * <version>...version...</version>
229 * <configuration>
230 * <username>Admin</username>
231 * <password>admin</password>
232 * <extensionIds>
233 * <extensionId>
234 * <id>org.xwiki.contrib.markdown:syntax-markdown-markdown12</id>
235 * <version>8.5.1</version>
236 * </extensionId>
237 * </extensionIds>
238 * </configuration>
239 * <executions>
240 * <execution>
241 * <id>install</id>
242 * <goals>
243 * <goal>install</goal>
244 * </goals>
245 * </execution>
246 * </executions>
247 * </plugin>
248 * }</pre>
Vincent Massol 30.2 249 {{/code}}
Vincent Massol 30.1 250
251 {{error}}
Nikita Petrenko 67.2 252 There's currently a [[bug in Checkstyle>>https://github.com/checkstyle/checkstyle/issues/3351]] that forces us to escape the ##<## as in:
Vincent Massol 30.1 253
Thomas Mortagne 31.1 254 {{code language="none"}}
Vincent Massol 30.4 255 * <pre><code>
256 * &#60;plugin&#62;
257 * &#60;groupId&#62;org.xwiki.platform&#60;/groupId&#62;
258 * &#60;artifactId&#62;xwiki-platform-tool-provision-plugin&#60;/artifactId&#62;
259 * &#60;version&#62;...version...&#60;/version&#62;
260 * &#60;configuration&#62;
261 * &#60;username&#62;Admin&#60;/username&#62;
262 * &#60;password&#62;admin&#60;/password&#62;
263 * &#60;extensionIds&#62;
264 * &#60;extensionId&#62;
265 * &#60;id&#62;org.xwiki.contrib.markdown:syntax-markdown-markdown12&#60;/id&#62;
266 * &#60;version&#62;8.5.1&#60;/version&#62;
267 * &#60;/extensionId&#62;
268 * &#60;/extensionIds&#62;
269 * &#60;/configuration&#62;
270 * &#60;executions&#62;
271 * &#60;execution&#62;
272 * &#60;id&#62;install&#60;/id&#62;
273 * &#60;goals&#62;
274 * &#60;goal&#62;install&#60;/goal&#62;
275 * &#60;/goals&#62;
276 * &#60;/execution&#62;
277 * &#60;/executions&#62;
278 * &#60;/plugin&#62;
Vincent Massol 30.1 279 * </code></pre>
Vincent Massol 30.2 280 {{/code}}
Vincent Massol 30.1 281 {{/error}}
282
Caleb James DeLisle 5.1 283 = Trailing Whitespace =
284
285 Trailing whitespace is prohibited except for one case.
286 In empty lines in a javadoc comment, a single trailing space character is acceptable but not required.
287
Thomas Mortagne 22.1 288 {{code language="java"}}
Caleb James DeLisle 5.1 289 /**
290 * The Constructor.
291 *
292 * $param something...
293 */
294 {{/code}}
295
Manuel Smeria 19.2 296 The trailing whitespace in the center line in that comment is permissible. See [[this proposal>>http://lists.xwiki.org/pipermail/devs/2010-October/020439.html]] for more information.
Caleb James DeLisle 5.1 297
Vincent Massol 3.1 298 = Class/Interface names =
Vincent Massol 1.1 299
Vincent Massol 2.1 300 * Prefix class names with ##Abstract## for abstract classes
Vincent Massol 1.1 301 * Class names should start with an uppercase letter
302 * The interface name should be as short and expressive as possible with no technical prefix or suffix. For example "Parser".
303 ** As a consequence interfaces shouldn't be prefixed with "I" (as in "IParser") or suffixed with "Interface" (as in "ParserInterface"), nor suffixed with "IF" (as in "ParserIF).
304 * Classes implementing interfaces should extend the interface name by prefixing it with a characteristic of the implementation. For example "XWikiParser".
305 ** As a consequence implementation classes shouldn't be suffixed with "Impl", "Implementation", etc.
306 * Default implementation classes where there's only one implementation provided by XWiki should be prefixed with "Default". As in "DefaultParser".
307
Vincent Massol 3.1 308 = Members and fields names =
Vincent Massol 1.1 309
Vincent Massol 2.1 310 * All methods and fields names should be camelCase, starting with a lower letter (##someProperty##, ##getSomeProperty()##)
311 * The names should be understandable and short, avoiding abbreviations (##parentDocument## instead of ##pdoc##, for example)
312 * Constants should all be uppercase, with underscores as word separators, and no prefix letter (##WIKI_PAGE_CREATOR## instead of ##WIKIPAGECREATOR##)
313 * Constants should be ##public/private static final## in classes (##public static final String CONTEXT_KEY = "theKey"##) and without any modifiers in interfaces, since ##public##, ##static## and ##final## are implied and enforced (##String PREFERENCES_DOCUMENT_NAME = "XWiki.XWikiPreferences"##)
Vincent Massol 1.1 314
Vincent Massol 3.1 315 = Package names =
Vincent Massol 1.1 316
Vincent Massol 11.1 317 * All code that is not located in the oldcore module should use ##org.xwiki##.
Vincent Massol 2.1 318 * The package name for code using the component-based architecture must be of the format ##org.xwiki.(module name).*##. For example ##org.xwiki.rendering##.
319 * Non user-public code must be located in an ##internal## package just after the module name. For example: ##org.xwiki.rendering.internal.parser.##**. General rule is ##org.xwiki.(module name).internal.##**
Thomas Mortagne 56.1 320 * [[Script Services>>extensions:Extension.Script Module]] component implementations should be located in a ##script## package and in a **non-internal package**. This is because they are considered API and we wish to have our backward-compatibility tool report any breakage (and also so that they are included in the generated Javadoc). If you still need to expose a script service in an internal package the class name should end with ##InternalScriptService## to not fail the build rule.
Vincent Massol 1.1 321
Vincent Massol 3.1 322 = Logging Best Practices =
Vincent Massol 1.1 323
Thomas Mortagne 22.1 324 * Use SLF4J. Specifically, if your code is in a Component it must get a Logger using the following construct:(((
Vincent Massol 21.1 325 {{code language="java"}}
Vincent Massol 9.1 326 import org.slf4j.Logger;
327 ...
328 @Inject
329 private Logger logger;
330 {{/code}}
Vincent Massol 21.1 331
332 If not inside a Component, a Logger can be retrieve through:
333
334 {{code language="java"}}
335 import org.slf4j.Logger;
336 ...
337 private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
338 {{/code}}
Vincent Massol 9.1 339 )))
Vincent Massol 1.1 340 * You should use the severity according to the following rules:
Vincent Massol 21.1 341 ** **info**: To be used when it’s absolutely necessary for the user to see the message in the logs. Usually only used at startup and we want to limit what the user sees to the absolute necessary to avoid swamping him. These logs are shown by default (with the default XWiki logging configuration).
342 ** **debug**: Logs that are informational but that shouldn't be printed by default. Logging configuration needs to be updated to show these logs.
Clément Aubin 28.2 343 ** **warning**: To be used when an error happens but it doesn’t compromise the stability and general working of an XWiki instance. A warning just shows the user that something has gone wrong and it should provide him with as much information as possible to solve the issue. Do not print a stack trace when you output a warning since stack traces fill the logs and should be reserved for errors. In the view of users stack traces are synonymous with errors. We want it to be easy for admins to visually check the log files and see important problems (i.e. errors). In order to display the root cause without displaying a full stack trace, use ##org.apache.commons.lang3.exception.ExceptionUtils##. Instead of writing:(((
Vincent Massol 21.1 344 {{code language="java"}}
Vincent Massol 21.2 345 LOGGER.warn("Failed to determine if the index exists: [{}]. Trying to recreate the index..", e.getMessage());
Vincent Massol 21.1 346 {{/code}}
347
348 write instead:
349
350 {{code language="java"}}
Vincent Massol 21.2 351 LOGGER.warn("Failed to determine if the index exists: [{}]. Trying to recreate the index..", ExceptionUtils.getRootCauseMessage(e));
Vincent Massol 21.1 352 {{/code}}
353
354 These logs are shown by default (with the default XWiki logging configuration).
355 )))
356 ** **error**: To be used when there’s an important problem that compromises the stability of the XWiki instance or that prevent an important system from working and that should not have happened. Always pass a stack trace when logging errors since it’s something that shouldn’t have happened an a developer will need to debug the problem to fix it. These logs are shown by default (with the default XWiki logging configuration).
Vincent Massol 10.2 357 * If you need to perform computations for passing parameters to the logging calls you should instead use the appropriate SLF4J signature [[to not incur performance penalty>>http://www.slf4j.org/faq.html#logging_performance]]. For example:(((
Vincent Massol 21.1 358 {{code language="java"}}
359 this.logger.debug("Test message with [{}] and [{}]", param1, param2);
Vincent Massol 10.1 360 {{/code}}
Vincent Massol 21.1 361
362 Do NOT write:
363
364 {{code language="java"}}
365 this.logger.debug("Test message with [" + param1 + "] and [" + param2 + "]", param1, param2);
366 {{/code}}
Vincent Massol 10.1 367 )))
Vincent Massol 21.1 368 * Always log as much information as possible to make it easier to understand what’s going on.
369 * Surround parameters with ##[]## in order to separate visually the text from the parameters and also clearly notice when leading/trailing spaces are located in parameters.
370
Denis Gervalle 4.1 371 = Imports =
372
373 * imports should be added individually for each class/interface
Vincent Massol 13.2 374 * individual imports should be grouped together and separated by blank lines following this model:(((
Vincent Massol 23.4 375 {{code language="java"}}
Denis Gervalle 4.1 376 import java.*
377
378 import javax.*
379
Thomas Mortagne 68.1 380 import jakarta.*
381
Denis Gervalle 4.1 382 import org.*
383
384 import com.*
385
386 import <any other imports>
387
388 import static <any static imports>
389 {{/code}}
Vincent Massol 13.2 390 )))
Thomas Mortagne 22.1 391 * The above code style settings for IntelliJ IDEA will automatically follow these rules, so you usually do not have to take care (Be careful that the default configuration for IntelliJ IDEA is not appropriate). For Eclipse you should import [[eclipse.importorder>>https://github.com/xwiki/xwiki-commons/blob/master/xwiki-commons-tools/xwiki-commons-tool-verification-resources/src/main/resources/eclipse.importorder]].
Vincent Massol 14.1 392
393 = Equals/HashCode and ToString implementations =
394
Manuel Smeria 19.2 395 We've decided to standardize on using Apache Commons Lang [[HashCodeBuilder>>http://commons.apache.org/proper/commons-lang//apidocs/org/apache/commons/lang3/builder/HashCodeBuilder.html]], [[EqualsBuilder>>http://commons.apache.org/proper/commons-lang//apidocs/org/apache/commons/lang3/builder/EqualsBuilder.html]] and [[ToStringBuilder>>http://commons.apache.org/proper/commons-lang//apidocs/org/apache/commons/lang3/builder/ToStringBuilder.html]].
Vincent Massol 14.1 396
397 For example:
398
399 {{code language="java"}}
400 ...
401 @Override
402 public boolean equals(Object object)
403 {
404 if (object == null) {
405 return false;
406 }
407 if (object == this) {
408 return true;
409 }
410 if (object.getClass() != getClass()) {
411 return false;
412 }
413 WikiBotListenerData rhs = (WikiBotListenerData) object;
414 return new EqualsBuilder()
415 .appendSuper(super.equals(object))
416 .append(getReference(), rhs.getReference())
417 .isEquals();
418 }
419
420 @Override
421 public int hashCode()
422 {
423 return new HashCodeBuilder(3, 17)
424 .appendSuper(super.hashCode())
425 .append(getReference())
426 .toHashCode();
427 }
428 ...
429 {{/code}}
Vincent Massol 16.1 430
Vincent Massol 16.3 431 XWiki provides a custom ##ToStringBuilder## implementation named ##XWikiToStringBuilder## that uses a custom XWiki's ##toString## style (see the [[Text Module>>extensions:Extension.Text Module]] for information).
Vincent Massol 16.1 432
433 For example:
434
435 {{code language="java"}}
436 ...
437 @Override
438 public String toString()
439 {
440 ToStringBuilder builder = new XWikiToStringBuilder(this);
441 builder = builder.append("Typed", isTyped())
442 .append("Type", getType().getScheme());
443
444 if (getReference() != null) {
445 builder = builder.append("Reference", getReference());
446 }
447
448 if (!getBaseReferences().isEmpty()) {
449 builder = builder.append("Base References", getBaseReferences());
450 }
451
452 Map<String, String> params = getParameters();
453 if (!params.isEmpty()) {
454 builder = builder.append("Parameters", params);
455 }
456
457 return builder.toString();
458 }
459 ...
460 {{/code}}
461
462 This example would generate the following:
463
464 {{code language="java"}}
465 ...
466 ResourceReference reference = new ResourceReference("reference", ResourceType.DOCUMENT);
467 Assert.assertEquals("Typed = [true] Type = [doc] Reference = [reference]", reference.toString());
468
469 reference.addBaseReference("baseref1");
470 reference.addBaseReference("baseref2");
471 Assert.assertEquals("Typed = [true] Type = [doc] Reference = [reference] "
472 + "Base References = [[baseref1], [baseref2]]", reference.toString());
473
474 reference.setParameter("name1", "value1");
475 reference.setParameter("name2", "value2");
476 Assert.assertEquals("Typed = [true] Type = [doc] Reference = [reference] "
477 + "Base References = [[baseref1], [baseref2]] "
478 + "Parameters = [[name1] = [value1], [name2] = [value2]]", reference.toString());
479 ...
480 {{/code}}
Vincent Massol 23.1 481
Thomas Mortagne 34.1 482 = Test classes =
483
Alexandru Brassat 68.4 484 We sometimes write complex tools in ##test## classes but those should never be used in ##main## code. While Maven accept it technically it's not the case of Eclipse for example.
Thomas Mortagne 34.1 485
Alexandru Brassat 68.4 486 If they really are needed then it's a sign that they should probably move to ##main## or that the test tool manipulating those classes should itself have its classes locate in ##test## (see [[xwiki-platform-test-page>>https://github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-test]] for an example for this use case).
Thomas Mortagne 36.1 487
488 = Script Services =
489
490 See [[Best practices for the Script Module>>extensions:Extension.Script Module#HBestPractices]].
Vincent Massol 50.1 491
492 = Deprecation =
493
494 {{version since="14.0RC1"}}
495 * Always use both the ##@Deprecated## annotation and the ##@deprecated## javadoc tag.
496 * In the ##@deprecated## javadoc tag, always specify WHY it’s deprecated and WHAT should be used instead.
Vincent Massol 51.1 497 * In the ##@Deprecated## annotation always use the ##since## parameter to specify WHEN it's been deprecated, and don't specify ##forRemoval## [[we don't break APIs in XWiki>>Community.DevelopmentPractices#HBackwardCompatibility]] and the default value is ##false##.
Vincent Massol 59.1 498 * Don't specify the since versions in the ##@deprecated## javadoc tag as it would be a duplication from the info in the ##@Deprecated## annotation, and the javadoc tool displays the content of the ##@Deprecated## annotation in the javadoc.
Vincent Massol 60.2 499 * If the deprecation is done in several branches, the ##since## parameter should use a comma-separated list of all versions in which the deprecation has been done. For example:(((
500 {{code language="java"}}
501 @Deprecated(since = "15.5RC1,14.10.12")
502 {{/code}}
503 )))
Vincent Massol 50.1 504
505 Example:
506
507 {{code language="java"}}
Vincent Massol 50.2 508 public class Worker
509 {
Vincent Massol 50.1 510 /**
Vincent Massol 50.2 511 * Calculate period between versions.
Vincent Massol 50.1 512 *
513 * @param machine the instance
514 * @return the computed time
Vincent Massol 50.2 515 * @deprecated This method is no longer acceptable to compute time between versions because... Use {@link Utils#calculatePeriod(Machine)} instead.
Vincent Massol 50.1 516 */
Vincent Massol 51.1 517 @Deprecated(since = "4.5")
Vincent Massol 50.2 518 public int calculate(Machine machine)
519 {
Vincent Massol 50.1 520 return machine.exportVersions().size() * 10;
521 }
522 }
523 {{/code}}
524 {{/version}}
Vincent Massol 63.1 525
526 = Optional =
527
Alexandru Brassat 68.4 528 Our rules around the Java ##Optional<>## class are:
Nikita Petrenko 67.2 529
Vincent Massol 63.1 530 * Always try to use Optional in return values for methods that can return null and don’t do it if there’s a good-enough reason (to be justified since it deviates from the best practice).
531 * Don't use Optional in return values of methods when these methods can be used from scripting (Velocity). Until [[this issue is fixed>>https://jira.xwiki.org/browse/XCOMMONS-1930]] at least.
532
Vincent Massol 64.1 533 = Quality check ignores =
534
535 XWiki executes different quality checks during the build, using different tools (checkstyle, spoon, sonarqube). Sometimes, some of these checks are either false positives or checks that we want to temporarily disable (not a good practice but it may happen). The instructions below provide a best practice approach for doing this.
536
537 == Checkstyle ==
538
539 * If the issue affects the whole class/interface/etc, use {{code language="none"}}@SuppressWarnings("checkstyle:<rule name here>"){{/code}} on the class/interface/etc.
540 * If the issue affects a whole method, use {{code language="none"}}@SuppressWarnings("checkstyle:<rule name here>"){{/code}} on the method.
541 * If the issue affects a variable declaration, use {{code language="none"}}@SuppressWarnings("checkstyle:<rule name here>"){{/code}} on the variable declaration.
542 * If the issue affects a line (basically whenever an annotation is not allowed), use {{code language="none"}}// CHECKSTYLE:<rule name>{{/code}} to ignore the next line or {{code language="none"}}// CHECKSTYLE:<rule name>(n){{/code}} to ignore the next ##n## lines.(((
543 Example:
Vincent Massol 64.2 544
Vincent Massol 64.1 545 {{code language="java"}}
546 @Override
547 InternalBinaryStringEncoder getEncoder()
548 {
549 return new AbstractBouncyCastleInternalBinaryStringEncoder(new HexEncoder(), BLOCK_SIZE, CHAR_SIZE)
550 {
551 @Override
552 public boolean isValidEncoding(byte b)
553 {
Vincent Massol 65.1 554 // Cryptography requires complex expressions, allow a few such expressions
Vincent Massol 64.1 555 // CHECKSTYLE:BooleanExpressionComplexity
556 return ((b >= 0x2f && b <= 0x39) || (b >= 0x41 && b <= 0x46) || (b >= 0x61 && b <= 0x66));
557 }
558 };
559 }
560 {{/code}}
561 )))
Nikita Petrenko 67.2 562 * (((
Nikita Petrenko 67.3 563 Always make sure to add a comment explaining why the ignore is there, using the following formats* When using an annotation:
564
565 (((
Vincent Massol 65.1 566 {{code language="java"}}
567 // This file has lists of strings copied from a source, making them constants would complicate updating from
568 // upstream.
569 @SuppressWarnings("checkstyle:MultipleStringLiterals")
570 public class HTMLDefinitions
571 ...
572 {{/code}}
573 )))
Nikita Petrenko 67.3 574
Nikita Petrenko 67.2 575 * When using a comment:(((
Vincent Massol 65.1 576 {{code language="java"}}
577 // Cryptography requires complex expressions, allow a few such expressions
578 // CHECKSTYLE:BooleanExpressionComplexity
579 return ((b >= 0x2f && b <= 0x39) || (b >= 0x41 && b <= 0x46) || (b >= 0x61 && b <= 0x66));
580 {{/code}}
581 )))
Nikita Petrenko 67.2 582 )))
Vincent Massol 64.1 583
Vincent Massol 66.1 584 == SonarQube ==
585
586 * Use the {{code language="none"}}@SuppressWarnings(<rule id>){{/code}} annotation wherever it’s valid to use it, i.e. everywhere except inside methods (except for variable declarations). Note that ##<rule id>## is the full Sonarqube issue id, e.g. ##java:S1133##.
587 ** Always add a comment to explain why it's considered a false positive or the reason to ignore the issue, using the format:(((
588 {{code language="java"}}
589 // Explanation here
590 @SuppressWarnings("java:S1133")
591 public final class ABC
592 {
593 ...
594 {{/code}}
595
596 Example:
597
598 {{code language="java"}}
599 @Override
600 // The token is generated and not user-controlled
601 @SuppressWarnings("javasecurity:S5145")
602 public boolean isTokenValid(String token)
603 {
604 ...
605 {{/code}}
606 )))
607 * For the places where the ##@SuppressWarnings## annotation is not allowed, continue using the SonarCloud UI for now until SonarQube implements a solution for this.
608 * Sometimes we also need to disable rules on several classes. For example right now we ignore the [[##java:S1133##>>https://sonarsource.github.io/rspec/#/rspec/S1133/java]] rule for all classes in ##**/xwiki-platform-legacy-*/**/*.java##. The rule is to define this in the ##xwiki-commons/xwiki-commons-pom.xml## inside the ##<properties>## section, as in:(((
609 {{code language="xml"}}
610 <!-- Tell SonarQube to not report usage of deprecated APIs in XWiki legacy code as it's fine there and in any
611 case we don't want to take the risk to perform refactorings on legacy code. -->
612 <sonar.issue.ignore.multicriteria>e1</sonar.issue.ignore.multicriteria>
613 <sonar.issue.ignore.multicriteria.e1.ruleKey>java:S1133</sonar.issue.ignore.multicriteria.e1.ruleKey>
614 <sonar.issue.ignore.multicriteria.e1.resourceKey>
615 **/xwiki-*-legacy-*/**/*.java
616 </sonar.issue.ignore.multicriteria.e1.resourceKey>
617 {{/code}}
Vincent Massol 67.1 618
Nikita Petrenko 67.2 619 {{info}}
620 When these ignores were declared in the SonarCloud UI, they were applied automatically to all branches, and now that we declare them in our ##pom.xml## we need to merge the changes to all branches or they'll fail on some branches
621 {{/info}}
Vincent Massol 66.1 622 )))

Get Connected